Swift 3与Swift 4中flatMap行为差异的技术问询
flatMap Behaves Differently with String Returns Great question! The behavior change you're seeing comes from a deliberate overhaul of the flatMap method in Swift 4, which cleaned up ambiguous overloads that caused confusion among developers. Let's break this down step by step:
What's Happening in Your Code
First, let's recap your example:
let myURLArray: ([URL]) = [ URL(string: "https://www.google.fr")! ] let result = myURLArray.flatMap { $0.absoluteString } print(result)
In Swift 3.2
The result is a [String] with output ["https://www.google.fr"]. This is because Swift 3.x included an overload of flatMap that effectively acted the same as map:
func flatMap<T>(_ transform: (Element) throws -> T) rethrows -> [T]
When your closure returns a non-optional, non-sequence type (like String here), the compiler picked this overload, so it just collected the returned strings into an array—no flattening occurred.
In Swift 4
The result becomes [String.Element] (i.e., [Character]) with output ["h", "t", "t", "p", "s", ":", "/", "/", "w", "w", "w", ".", "g", "o", "o", "g", "l", "e", ".", "f", "r"]. Here's why:
Apple removed the flatMap overload that mirrored map to eliminate ambiguity. The only remaining flatMap overload for sequences is the one designed to flatten nested sequences:
func flatMap<S: Sequence>(_ transform: (Element) throws -> S) rethrows -> [S.Element]
Since String conforms to Sequence (its elements are Characters), the compiler now uses this overload. It takes each String returned by your closure, flattens it into its individual characters, and collects all those characters into a single array.
Why This Change Was Made
The old flatMap had overlapping responsibilities: it handled both optional unwrapping/filtering and sequence flattening, plus the redundant overload that acted like map. This led to unexpected behavior when developers intended one operation but got another.
In Swift 4, Apple split these responsibilities clearly:
- Use
mapwhen you just want to transform each element and keep the same array structure. - Use
compactMap(replaces the optional-handlingflatMapoverload) when you want to transform elements and filter outnilvalues. - Use
flatMaponly when you want to transform elements into sequences and flatten the result.
Fixing Your Code in Swift 4
If you want the Swift 3.2 behavior (a [String] array) in Swift 4, simply use map instead of flatMap:
let result = myURLArray.map { $0.absoluteString }
This will give you the expected ["https://www.google.fr"] output.
内容的提问来源于stack exchange,提问作者Mick F

