I happen to see some code with map with parentheses seems implicitly passing item to init. I'm confused how it is working properly. Would anyone explain me about this grammar or cite any doc for me?
class User: NSObject {
let name: String
init(name: String) {
self.name = name
}
}
let names: [String] = ["John", "Bob", "Max"]
let users: [User] = names.map( // parentheses instead of curly brackets
User.init // is name passed to init implicitly?
)
users.forEach {
print($0.name) // name is inserted properly
}
CodePudding user response:
If you option-click on map
in Xcode, you will see that it is a function with this declaration:
func map<T>(_ transform: (Self.Element) throws -> T) rethrows -> [T]
map
takes one parameter which is closure (or function) that takes an element of the sequence it is called on and returns a value of the same or another type T
. map
calls that closure/function for each element and builds an Array
of that type T
(aka [T]
).
User.init
is a function which is of the type (String) -> User
. It takes a String
and transforms it into a User
.
So calling:
let users: [User] = names.map(User.init)
has the same effect as calling:
let users: [User] = names.map { User(name: $0) }
which is using trailing closure syntax to pass a closure of type (String) -> User
to map()
.
A way to get from here to there
Make the following assignments:
let x = User.init // this is shorthand for User.init(name:)
let y = { User(name: $0) }
and option-click on x
and y
. In both cases, they represent a closure of type (String) -> User
and both do the same thing.
You can call x("Bob")
and y("Bob")
to create a User
with name
"Bob"
.
If you start with the form you are most used to seeing:
let users: [User] = names.map { User(name: $0) }
and remove the trailing closure syntax:
let users: [User] = names.map({ User(name: $0) })
and then substitute the equivalent closure, you arrive at the other style of map
:
let users: [User] = name.map(User.init)
Note: Swift allows the shorthand notation User.init
because there is only one initializer for User
. If User
had a second initializer that took an Int
for example, then you'd have to specify the parameter name to remove the ambiguity when assigning to the variable x
, but not when using it with map
because Swift is smart and knows that you called map
on [String]
, so the transform function must be one that takes a String
.