Key path cannot refer to '...', which has a mutating getter

This is in regard to a lazy var.
What is the reason behind this restriction?

It would be helpful if you posted the code that's producing that error.

1 Like

This is very much a strawman example, but...

struct S {
    var i = 1
    lazy var j = 1
}

var s = S()
let si = \S.i
print(s[keyPath: si])
let sj = \S.j  // this is the line with the error
print(s[keyPath: sj])

lazy1.swift:9:13: error: key path cannot refer to 'j', which has a mutating getter

First, lazy vars generate a mutating getter on structs:

struct S {
    var i = 1
    lazy var j = 1
}

let s = S()
s.j // Cannot use mutating getter on immutable value: 's' is a 'let' constant

Second, the getter of the [keyPath:] subscript is not mutating.

Therefore, the [keyPath:] subscript can not accept a key path to a mutating getter.

Therefore, the key path implementors did not bother implementing key paths to mutable getters. They would have been a dead-end anyway.

A possible workaround is to use a class, or a a private inner storage class:

class S {
    lazy var j: Int = 1
}

let s = S()
let sj = \S.j  // no error
print(s[keyPath: sj])

// ---------------------------------------------
struct S {
    private class Storage {
        lazy var j = 1
    }
    private let storage = Storage()
    
    var j: Int {
        get { return storage.j }
        set { storage.j = newValue }
    }
}

let s = S()
let sj = \S.j  // no error
print(s[keyPath: sj])

Why is it a dead end? Shouldn't Swift be fixed/amended with a key path subscript that takes a mutating getter (or generalized to take either kind of getter)?

Supporting mutating getters would require a combinatorial explosion of key-path types for very little gain. We currently have five key path types: AnyKeyPath (immutable, fully type-erased), PartialKeyPath (immutable, type-erased value), KeyPath (immutable), WritableKeyPath (mutable, mutating setter), and ReferenceWritableKeyPath (mutable, non-mutating setter). To support mutating getters we would need to double this (with the possible exception of AnyKeyPath). It's a corner case that clearly doesn't justify the increase in complexity.

1 Like

We had a similar combinatorial explosion when exploring throwing writable key paths in Extract Payload for enum cases having associated value. We have difficulties extending the key path model.

Terms of Service

Privacy Policy

Cookie Policy