0
    struct System {
        var method: (() -> ())?
        var curMethod: Int

        init() {
            method = nil
curMethod = 0
        }

        mutating func method1() {
            curMethod = 1
        }

        mutating func method2() {
            curMethod = 2
        }
    }

    var sys = System()
    sys.method = System.method1
    sys.method!()

I get an error cannot assign value of type (inout System) -> () -> ()' to type '(() -> ())?. What am I doing wrong?

7
  • struct System { var method: (() -> ())? var curMethod: Int init() { method = nil curMethod = 0 } mutating func method1() { curMethod = 1 } mutating func method2() { curMethod = 2 } } var sys = System() if let method1 = sys.method { sys.method = sys.method1 } sys.method!() print(sys.curMethod) //should be "1" Commented Oct 5, 2017 at 16:45
  • Now it's ok. Thanks! Commented Oct 5, 2017 at 16:50
  • Well, it's not really what I wanted to solve, sorry)) instead of calling sys.method1() I want to call sys.method() which should be == sys.method1() Commented Oct 5, 2017 at 16:53
  • 1
    What's the intention for this code? It looks like it could be improved with enum and protocol Commented Oct 5, 2017 at 17:14
  • 1
    You could do something like this, but there's almost certainly a better way to solve whatever concrete problem you're facing. Commented Oct 5, 2017 at 21:53

2 Answers 2

1

First of all, your line sys.method = System.method1 is wrong, as it would require method1 to be a static function (like a class function) rather than an instance function, so it should be changed to sys.method = sys.method1. But this isn't allowed - the error is "error: partial application of 'mutating' method is not allowed".

If you make System a class (rather than a struct), it will work if you replace the System.method1 with sys.method1.

The reason for this is that a mutating func is actually quite a lot more than a simple function under the hood - it is a curried function (curried with a compiler generated function) that effectively creates a new copy of the struct with the "new" value - hence, you A) can't access it it directly ("partial application is not allowed") and B) you can't assign it to a ()->() variable.

Sign up to request clarification or add additional context in comments.

2 Comments

mutating funcs don't create a copy of the instance to operate, and that's exactly why there is the "partial application is not allowed". Allowing that to be possible would effectively introduce reference semantics and shared mutability to structures, which is exactly what they're designed to prevent
mutating methods are actually fairly simple under the hood; the implicit self parameter is just inout, so is passed by reference. That reference is only guaranteed to be valid until the function call ends, so attempting to prolong that window of mutation by partially applying into a () -> Void is illegal. And in fact, there's actually currently a hole in the compiler where you can escape that reference with the curried instance method when accessed on the type: var sys = System(); let curried = System.method1; let unsafe = curried(&sys); unsafe(). That's UB. SE-0042 will fix it tho.
0

So, there're 3 variants suggested by participants. Everything is working, and using class instead of struct seems to me less complicated.

struct System1 {

    var method: (() -> ())?
    var curMethod: Int

    init() {
        method = nil
        curMethod = 0
    }

    mutating func method1() { curMethod = 1 }
    mutating func method2() { curMethod = 2 }

}

struct System2 {

    var method: ((inout System2) -> ())?
    var curMethod: Int

    init() {
        method = nil
        curMethod = 0
    }

    mutating func callCurrentMethod() { method?(&self) }
    mutating func method1() { curMethod = 1 }
    mutating func method2() { curMethod = 2 }

}

class System3 {

    var method: (() -> ())?
    var curMethod: Int

    init() {
        method = nil
        curMethod = 0
    }

    func method1() { curMethod = 1 }
    func method2() { curMethod = 2 }

}

var struct1 = System1()
var struct2 = System2()
var class1 = System3()

print(struct1.curMethod)
let curried = System1.method1
let unsafe = curried(&struct1)
unsafe()
print(struct1.curMethod)

print(struct2.curMethod)
struct2.method = { $0.method1() }
struct2.callCurrentMethod()
print(struct2.curMethod)

print(class1.curMethod)
class1.method = class1.method1
class1.method!()
print(class1.curMethod)

4 Comments

The first one is undefined behaviour; don't actually use it. I only mentioned it in my comment above as it was an interesting hole in the compiler. Also note that the example with the class creates a retain cycle, you'll likely want to say class1.method = { [weak class1] in class1?.method1() } instead.
If I use class1.method = class1.method1, I can do the following: var methods = [System3.method2]; class1.method = methods[0](class1); How would you do the same with weak?
It wouldn't be pretty; something like class1.method = { [weak class1, m0 = methods[0]] in guard let class1 = class1 else { return }; m0(class1)() }. You could simplify with unowned, but you'll have to ensure you satisfy the preconditions that come with that. Although now that I think about it, a better way to avoid the retain cycle would be just to store the curried function on the instance.
But again, I still maintain that there's probably a better way to solve whatever concrete problem you're facing. But without knowing what it is, it's impossible to say.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.