I just learned that mutating func is just a curried func with first parameter as inout, so the code below will work and change firstName
to "John"
struct Person {
var firstName = "Matt"
mutating func changeName(fn: String) {
firstName = fn
}
}
var p = Person()
let changer = Person.changeName
changer(&p)("John")
p.firstName
but the odd thing happend when I add property observer to p
like below, you can see firstName
is still "Matt", why?
An interesting note to take ist that the observer is called before the curried setter is called:
struct Person {
var firstName = "Matt"
mutating func changeName(fn: String) {
firstName = fn
}
}
var p: Person = Person() {
didSet {
print("p was set")
}
}
print("1: \(p.firstName)")
let changer = Person.changeName
print("2: \(p.firstName)")
let setter = changer(&p)
print("3: \(p.firstName)")
setter("John")
print("4: \(p.firstName)")
p.changeName("John")
print("5: \(p.firstName)")
This prints:
1: Matt
2: Matt
p was set
3: Matt
4: Matt
p was set
5: John
So it seems that acquiring the setter method on the inout struct performs the actual mutation. This is explained by how inout
parameters work semantically: When the parameter is passed to the function its value is copied to a place where the function can mutate it. When the function returns, the value is copied back to the original place, triggering setter observers once, whether the value did change or not.
When we change the way to get the pre-filled curried setter to:
let setter = p.changeName
... the compiler objects:
error: partial application of 'mutating' method is not allowed
It seems that the compiler understands that closing over an inout value is a bad idea, as it is basically taking a reference on a value type.
The closure would let you change the value of the struct at any time, even when the compiler assumes it to be constant. To prevent this unfortunate situation, the compiler simply forbids closing over the inout.
You found a case which fools the compiler and works around the diagnostic. This seems to be an error and it should be filed.
Short version:
struct Foo {
mutating func foo() {}
}
var f = Foo()
let m = Foo.foo
let s = m(&f)
One of the last two lines should emit an error, similar to let x = f.foo
.
in the newest accepted proposal 0042-flatten-method-type, self
is no more passed as curried function, so this problem is solved in the Swift 3
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With