The signature of Sequence.map()
is
func map<T, E>(_ transform: (Self.Element) throws(E) -> T) throws(E) -> [T] where E : Error
Clearly this is a throwing function.
However, when I specify a non-throwing function as its argument, it seems I can call map()
without try
:
let l = [1, 2, 3].map { $0 * 2 }
print(l) //=> [2, 4, 6]
Similarly, the signature of RangeReplaceableCollection.filter()
is
func filter(_ isIncluded: (Self.Element) throws -> Bool) rethrows -> Self
but I can call filter()
without try
if the argument will never throw:
let l = [1, 2, 3].filter { $0 % 2 == 1 }
print(l) //=> [1, 3]
The question is "why" or "how does it work under the hood"? Where is this behavior defined in the language reference? Because I want a strict understanding about the language, I added #language-lawyer
tag.
This is a typed throws
. map
throws errors of type E
, which is one of the generic type parameters. You don't need to try
because E
is inferred to be Never
, and that is because the closure you passed to map
is not a throwing closure.
If you had passed a closure that can throw, you will need to add try
to the filter
call too.
// the first 'try' is necessary
try [1, 2, 3].map { try someTransform($0) }
The language reference says:
Just like you can write a function that never returns with a return type of
Never
, you can write a function that never throws withthrows(Never)
:func nonThrowingFunction() throws(Never) { // ... }
This function can’t throw because it’s impossible to create a value of type
Never
to throw.
in this section.
I think the Swift Evolution proposal for this feature puts this more clearly:
Typed throws generalizes over both untyped throws and non-throwing functions. A function specified with
any Error
as its thrown type:func throwsAnything() throws(any Error) { ... }
is equivalent to untyped throws:
func throwsAnything() throws { ... }
Similarly, a function specified with
Never
as its thrown type:func throwsNothing() throws(Never) { ... }
is equivalent to a non-throwing function:
func throwsNothing() { }
filter
is marked rethrows
, not throws
. This section in the language reference talks about rethrowing functions
A function or method can be declared with the
rethrows
keyword to indicate that it throws an error only if one of its function parameters throws an error. These functions and methods are known as rethrowing functions and rethrowing methods.
Again, you don't need to try
for filter
because the closure you pass to it is not a throwing closure. If you had passed a closure that can throw, you will need to add try
to the filter
call too.
// the first 'try' is necessary
try [1, 2, 3].filter { try someCondition($0) }