Storing/passing Function Types from swift protocol

2019-01-20 20:29发布

问题:

This bit of code crashes the swift (3, 3.1, 4) compiler:

protocol Test {
    func f()
}
let x = Test.f // crash

I would expect, perhaps naively, that the compiler would infer x as a Function Type with the signature (Test) -> (Void) -> Void, and that later on, I could call it like so:

let y = SomeClassConformingToTest()
x(y)()

I guess my question is: clearly the compiler should do something other than crash, but should Swift currently support this syntax?

回答1:

As you say, the compiler should never crash; this is indeed a bug, and has been filed here. In it, Slava Pestov, a member of the Swift team, says:

We plan on making MyProtocol.someInstanceMethod work. You can already do this for classes, eg,

class Foo {
    func f() { ... }
}
let f: Foo -> () -> () = Foo.f

It should have the same behavior for protocols:

protocol Foo {
    func f()
}
let f: Foo -> () -> () = Foo.f

I plan on addressing this later.

And the bug report, as of the 8th of May 2017, is now marked as "In Progress", so hopefully this is something that will make it into the release build of Swift 4.0.

However, until implemented/fixed – a simple workaround is just to use a closure expression in order to act as a thunk for the partial application of the method with the instance to call it on:

protocol Test {
    func f()
}

struct S : Test {
    func f() {
        print("hello")
    }
}

let x: (Test) -> () -> Void = { $0.f }

let s = S()
x(s)() // "hello"

and of course, if you don't need the intermediate partially applied function, you can just say:

let x: (Test) -> Void = { $0.f() }

let s = S()
x(s) // "hello"