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

时间:2015-12-04 03:57:49

标签: swift functional-programming 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
}

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

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

1 个答案:

答案 0 :(得分:6)

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

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

func a(x: () throws -> ()) rethrows {
  try x()
}

所以a(x)仅在x()抛出时抛出。

如果你有函数b抛出:

func b() throws {
}

然后你必须通过try:

来调用a(b)
try a(b)
a(b) // gives "Call can throw but is not marked with 'try'"

但是如果你传递给a没有投掷功能:

func c() {
}

然后Swift编译器足够聪明,可以确定x()函数体中的a实际上没有抛出,它只允许调用a(c)

a(c) // it's ok

这是因为:

  

throws关键字是函数类型的一部分,也是非throwing的一部分   函数是抛出函数的子类型。因此,您可以使用   非投掷功能与投掷功能相同。

回到你的例子。

Optional.flatMap()定义为:

public func flatMap<U>(@noescape f: (Wrapped) throws -> U?) rethrows -> U?

在您的示例中f.flatMap的类型为((T -> U) throws -> U?) rethrows -> U?

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

Optional.map定义为:

public func map<U>(@noescape f: (Wrapped) throws -> U) rethrows -> U?

因此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调用。