我正在检查 .lazy
的高阶函数,发现了一些与 flatMap 函数(可能还有其他函数)相关的有趣的编译错误
示例
let array = [1, 2, 3, 4, 5, 6] array .flatMap { print("DD") return $0 // Cannot convert return expression of type 'Int' to return type 'String?' } .forEach { print("SS") print($0) }
注释掉一点
array .flatMap { // print("DD") return $0 } .forEach { print("SS") print($0) }
一切正常.. 更有趣的例子
array .flatMap { let z = $0 return $0 // Or return z - all is the same "Cannot convert return expression of type 'Int' to return type 'String?'" } .forEach { print("SS") print($0) }
什么会导致这种行为?
最佳答案
当前(从 Swift 4 开始)Sequence
上的 flatMap(_:)
方法有两种不同的含义:
它可以采用一个返回可选
T?
的转换闭包,它会返回一个[T]
,过滤掉nil
结果(此重载 is to be renamed 到compactMap(_:)
在未来的版本中)。public func flatMap<ElementOfResult>( _ transform: (Element) throws -> ElementOfResult? ) rethrows -> [ElementOfResult]
它可以接受一个返回
Sequence
的转换闭包,并且它将返回一个包含所有结果序列串联的数组。public func flatMap<SegmentOfResult : Sequence>( _ transform: (Element) throws -> SegmentOfResult ) rethrows -> [SegmentOfResult.Element]
现在,在 Swift 4 中,String
变成了 RangeReplaceableCollection
(因此变成了 Sequence
)。所以执行此操作的 Swift 3 代码:
// returns ["foo"], as using the `nil` filtering flatMap, the elements in the closure
// are implicitly promoted to optional strings.
["foo"].flatMap { $0 }
现在这样做:
// returns ["f", "o", "o"], a [Character], as using the Sequence concatenation flatMap,
// as String is now a Sequence (compiler favours this overload as it avoids the implicit
// conversion from String to String?)
["foo"].flatMap { $0 }
为了保持源兼容性,专门的 flatMap
重载了 were added对于字符串:
//===----------------------------------------------------------------------===// // The following overloads of flatMap are carefully crafted to allow the code // like the following: // ["hello"].flatMap { $0 } // return an array of strings without any type context in Swift 3 mode, at the // same time allowing the following code snippet to compile: // [0, 1].flatMap { x in // if String(x) == "foo" { return "bar" } else { return nil } // } // Note that the second overload is declared on a more specific protocol. // See: test/stdlib/StringFlatMap.swift for tests. extension Sequence { @_inlineable // FIXME(sil-serialize-all) @available(swift, obsoleted: 4) public func flatMap( _ transform: (Element) throws -> String ) rethrows -> [String] { return try map(transform) } } extension Collection { @_inlineable // FIXME(sil-serialize-all) public func flatMap( _ transform: (Element) throws -> String? ) rethrows -> [String] { return try _flatMap(transform) } }
以上用法在 Swift 3 兼容模式下仍会返回 [String]
,但在 Swift 4 中会返回 [Character]
。
那么,为什么
let array = [1, 2, 3, 4, 5, 6]
array
.flatMap {
print("DD")
return $0 // Cannot convert return expression of type 'Int' to return type 'String?'
}
.forEach {
print("SS")
print($0)
}
告诉你闭包应该返回一个 String?
?
好吧,Swift 目前不推断多语句闭包的参数和返回类型(有关更多信息,请参阅 this Q&A)。因此 flatMap(_:)
重载闭包返回通用 T?
或通用 S : Sequence
不符合条件在没有显式类型注释的情况下调用,因为它们需要类型推断来满足通用占位符。
因此,唯一符合条件的重载是特殊的 String
源兼容性重载,因此编译器期望闭包返回 String?
。
要解决此问题,您可以显式注释闭包的返回类型:
array
.flatMap { i -> Int? in
print("DD")
return i
}
.forEach {
print("SS")
print($0)
}
但如果您实际上没有在实际代码中使用此 flatMap(_:)
重载的可选过滤功能,您应该使用 map(_:)
相反。
关于arrays - flatMap 中的 "Cannot convert return expression",其中包含无意义的表达式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47968342/