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 - #233 by sveinhal. We have difficulties extending the key path model.