Swift`rethrows`函数作为参数传递导致编译器错误

2 functional-programming swift swift2

在Swift 2中,即使函数中没有任何调用可以抛出,下面的函数也不会编译.

func function1<T, U>(f: Optional<T -> U>, x: Optional<T>) -> Optional<U> {
    return f.flatMap(x.map) // Call can throw, but it is not marked with 'try' and the error is not handled
}
Run Code Online (Sandbox Code Playgroud)

此版本的函数与第一个版本相同(并且更详细),但它编译.

func function2<T, U>(f: Optional<T -> U>, x: Optional<T>) -> Optional<U> {
    return f.flatMap { g in
        x.map(g)
    }
}
Run Code Online (Sandbox Code Playgroud)

mix*_*xel 6

这些版本不完全相同,我解释原因.

假设你有函数,rethrows因为它接受抛出函数作为参数:

func a(x: () throws -> ()) rethrows {
  try x()
}
Run Code Online (Sandbox Code Playgroud)

所以a(x)只有投掷才会x()抛出.

如果你有b抛出的功能:

func b() throws {
}
Run Code Online (Sandbox Code Playgroud)

然后你必须a(b)用try 调用:

try a(b)
a(b) // gives "Call can throw but is not marked with 'try'"
Run Code Online (Sandbox Code Playgroud)

但如果你传递给a不投掷功能:

func c() {
}
Run Code Online (Sandbox Code Playgroud)

然后斯威夫特编译器足够聪明,以确定x()a函数体实际上不扔,它允许简单地调用a(c):

a(c) // it's ok
Run Code Online (Sandbox Code Playgroud)

这是因为:

throws关键字是函数类型的一部分,nonthrowing函数是抛出函数的子类型.因此,您可以在与抛出函数相同的位置使用非抛出函数.

回到你的例子.

Optional.flatMap() 定义为:

public func flatMap<U>(@noescape f: (Wrapped) throws -> U?) rethrows -> U?
Run Code Online (Sandbox Code Playgroud)

在你的例子中f.flatMap有类型((T -> U) throws -> U?) rethrows -> U?.

你看到它重新抛出,因为它接受抛出函数参数并且必须被调用,try除非f有类型Wrapped -> U?并且不抛出.

Optional.map 定义为:

public func map<U>(@noescape f: (Wrapped) throws -> U) rethrows -> U?
Run Code Online (Sandbox Code Playgroud)

所以x.map有型(T throws -> U) rethrows -> U?.

当你调用f.flatMap { g in x.map(g) }编译器看到它gT -> U类型并且不抛出时x.map(g),{ g in x.map(g) }并且f.flatMap { g in x.map(g) }都是安全的并且不抛出.

但是,当你调用f.flatMap(x.map)所有编译器看到的那个x.map(T throws -> U) rethrows -> U?类型并且可以(重新)抛出因此f.flatMap(x.map)不安全而且可以(重新)抛出并且必须被调用时try.