And @escaping in different contexts

So we have three essential types

struct User {
    var name = ""
}

struct Storage {
    public var users = [User]()
    public init() {}
}

struct Updater {
    let handler: ([User]) -> ()
    init(handler: @escaping ([User]) -> ()) {
        self.handler = handler
    }
    mutating func update() {
        let user = User(name: "Zhana")
        handler([user, user])
    }
}

So now we are in main.swift file. And this is work.

var storage = Storage()
var test = Updater() { newusers in
    storage.users = newusers
}
test.update()

And this is not work.

struct Engine {
    
    var st = Storage()
    
    mutating func update() {
        var test = Updater() { newusers in
            st.users = newusers // Closure cannot implicitly capture a mutating self parameter
        }
        test.update()
    }
}

Why? Because
var storage = Storage()
is a value. And an instance of Engine intends to be a value too.

You cannot capture self in a mutating method in an escapable closure.

If this were allowed, then there is the possibility that self may not exist by the time the closure executes.

You can fix this by either removing @escaping, or you change the value types to reference types (i.e., change your struct to class).

But why I can't modify a global context in which I would define the instance of structure Engine through an escaping closure? And at same time I can modify an instance of class Engine in the heap through an escaping closure.

I think that’s one of the main advantages of value types such as structures – since you can’t mutate them “from a distance”, they are much easier to reason about?

1 Like

But what is distance in this case? Is only order of invoke operations.

If you had an escaping closure that could modify the value you could store the closure and invoke it anytime later (think dispatch_after, for example), mutating the value. Then the whole guarantee about value types would go out of the window, wouldn’t it?

But in the book we read about clouser can store refer at the values from surround context even if the original scope no longer exist. At over that time a can not remember all treats of this chapter.

Which of the value type should be changed to reference type

The one that escapes the function body; in this case, self. This ensures the escaping closure has ownership (i.e., it increases the reference count of the instance). Thus, if self is deleted, the escaping closure still has ownership and will not fail when it finally executes.

You might want to read this: https://github.com/apple/swift/blob/master/docs/OwnershipManifesto.md

2 Likes