After searching for some references to figure it out, -unfortunately- I could not find useful -and simple- description about understanding the differences between throws and rethrows. It is kind of confusing when try to understand how we should use them.
I would mention that I am kind of familiar with the -default- throws with its simplest form for propagating an error, as follows:
enum CustomError: Error {
case potato
case tomato
}
func throwCustomError(_ string: String) throws {
if string.lowercased().trimmingCharacters(in: .whitespaces) == "potato" {
throw CustomError.potato
}
if string.lowercased().trimmingCharacters(in: .whitespaces) == "tomato" {
throw CustomError.tomato
}
}
do {
try throwCustomError("potato")
} catch let error as CustomError {
switch error {
case .potato:
print("potatos catched") // potatos catched
case .tomato:
print("tomato catched")
}
}
So far so good, but the problem arises when:
func throwCustomError(function:(String) throws -> ()) throws {
try function("throws string")
}
func rethrowCustomError(function:(String) throws -> ()) rethrows {
try function("rethrows string")
}
rethrowCustomError { string in
print(string) // rethrows string
}
try throwCustomError { string in
print(string) // throws string
}
what I know so far is when calling a function that throws it has to be handled by a try, unlike the rethrows. So what?! What is logic that we should follow when deciding to use throws or rethrows?
From "Declarations" in the Swift book:
Rethrowing Functions and Methods
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. Rethrowing functions and
methods must have at least one throwing function parameter.
A typical example is the map method:
public func map<T>(_ transform: (Element) throws -> T) rethrows -> [T]
If map is called with a non-throwing transform, it does not throw
an error itself and can be called without try:
// Example 1:
let a = [1, 2, 3]
func f1(n: Int) -> Int {
return n * n
}
let a1 = a.map(f1)
But if map is called with a throwing closure then itself can throw
and must be called with try:
// Example 2:
let a = [1, 2, 3]
enum CustomError: Error {
case illegalArgument
}
func f2(n: Int) throws -> Int {
guard n >= 0 else {
throw CustomError.illegalArgument
}
return n*n
}
do {
let a2 = try a.map(f2)
} catch {
// ...
}
If map were declared as throws instead of rethrows then you would
have to call it with try even in example 1,
which is "inconvenient" and bloats the code unnecessary.
If map were declared without throws/rethrows then you could not
call it with a throwing closure as in example 2.
The same is true for other methods from the Swift Standard Library
which take function parameters: filter(), index(where:), forEach() and many many more.
In your case,
func throwCustomError(function:(String) throws -> ()) throws
denotes a function which can throw an error, even if called with
a non-throwing argument, whereas
func rethrowCustomError(function:(String) throws -> ()) rethrows
denotes a function which throws an error only if called with a
throwing argument.
Roughly speaking, rethrows is for functions which do not throw
errors "on their own", but only "forward" errors from their function
parameters.
Just to add something along with Martin's answer. A non throwing function with the same signature as a throwing function is considered a sub-type of the throwing function. That is why rethrows can determine which one it is and only require try when the func param also throws, but still accepts the same function signature that doesn't throw. It's a convenient way to only have to use a do try block when the func param throws, but the other code in the function doesn't throw an error.
Related
I have an array of throwing completion block handlers:
typealias CompletionBlock = (MyType) throws -> Void
private var handlers: [CompletionBlock] = []
I want to be able to recall them later on and throw an error like this:
for handler in handlers {
handler(throw myError)
}
However, that doesn't work because it's expecting me to pass MyType to the handler, not an error. How can I do this?
Your question is a bit vague, but I'm going to assume you mean that you want the handlers to accept the result of a throwing function. That's possible, and I'll show how, but it's not quite what you want.
"The result of a throwing function" can be expressed by calling a function that calls a throwing function:
typealias CompletionBlock = (() throws -> MyType) -> Void
Throwing an error in the way you've described like this:
let result: () throws -> MyType = { throw MyError() }
for handler in handlers {
handler(result)
}
A handler would then look like:
func h(result: () throws -> MyType) {
do {
let myType = try result()
// ...
} catch {
// ...
}
}
But this is not a great design. Most importantly it executes result many times. Instead you want to pass the Result of a throwing function:
typealias CompletionBlock = (Result<MyType, Error>) -> Void
let f: () throws -> MyType = { throw MyError() }
let result = Result(catching: f)
for handler in handlers {
handler(result)
}
The more common look for this is along these lines:
let result = Result {
// Do various operations that might throw
return value
}
You can also convert back from Result to a throws using try result.get(). So you can easily move between Result and throws wherever you like. Result is best for storing. Throws is best for calling. You can have both.
(And of course, you should also be looking to the future and exploring replacing completion handlers with async/await, but there are many reasons you might not do that right away, so Result will be an important tool for quite some time.)
You are trying to change the behaviour of a stored closure by forcing it to throw an error regardless of its input. You cannot do that.
The fact that you marked CompletionBlock as throws only means that the closure itself might throw. However, you cannot force it to throw, since you can only pass an input argument of type MyType to it.
Moreover, there's no point in enforcing a closure to throw, since you need to handle the error thrown from the call-site anyways. So you can just simply throw the error from the call site.
Alternatively, if you need the closure itself to handle an error, you should change the closure to accept a Result and in case .failure in injected, the closure itself could throw.
typealias CompletionBlock = (Result<MyType, MyError>) throws -> Void
private var handlers: [CompletionBlock] = []
for handler in handlers {
handler(.failure(myError))
}
Is there a way in Swift to have a parameter accept a throwing function that returns a tuple as a parameter?
For example, I have this code where sendNewInvitation is a throwing function returns a tuple (UserProfile, [GroupMembership]).
I mean to pass this in as a parameter for updateUserMemberships, and the easiest way I can think of to do it is to set a separate variable as the result of sendNewInvitation, and then set each part of the tuple.
But I intend to have updateUserMemberships accept the results of several other functions, and I'm wondering if there is a way to do all of this in one line?
Current Code:
do {
let userAndMembership = try sendNewInvitation(user: user, group: group)
try updateUserMemberships(user: userAndMembership.0, groupMemberships: userAndMembership.1)
} catch {
print(error.localizedDescription)
return
}
Ideally, I'm wondering if there's a way to do something like this
(within the do block):
try updateUserMemberships(tuple: try sendNewInvitation)
(And I could redeclare the function to accept the parameters either as separate parameters or as a tuple of this type).
There is no problem calling a throwing function inside the call to another function, if the first function throws the catch clause will be entered and the second function never gets called. You can simply redefine the function to take a tuple instead
func updateUserMemberships(userInfo: (UserProfile, [GroupMembership])) throws {
...
}
And call the other function inside the call to `updateUserMemberships
do {
try updateUserMemberships(userInfo: try sendNewInvitation(user: user, group: group))
} catch {
print(error.localizedDescription)
return
}
You can create a type alias for your tuple if it helps improve readability
typealias UserInfo = (UserProfile, [GroupMembership])
func sendNewInvitation(user: String, group: String) throws -> UserInfo {
...
}
func updateUserMemberships(userInfo: UserInfo) throws {}
But if this is a type you are going to use in many places then I suggest you wrap it in a struct instead
struct UserInfo {
var userProfile: UserProfile
var groupMembership: [GroupMembership])
}
You can just pass the result of a throwing function as a parameter to another function, try is only needed once for the entire expression. In the following example, if foo() throws then bar() is not called and execution goes to the catch block:
func foo(n: Int) throws -> (Int, Int, Int) {
if n == 0 {
throw NSError(domain: "domain", code: 13, userInfo: nil)
}
return (1, 2, 3)
}
func bar(tuple: (Int, Int, Int)) {
print(tuple)
}
do {
try bar(tuple: foo(n: 0))
print("success")
} catch {
print(error)
}
rethrows to the rescue!
func sendNewInvitation(...) throws -> (UserProfile, [GroupMembership]) {
...
}
func updateUserMemebership(yourFunction: (...) throws -> (UserProfile, [GroupMembership])) rethrows {
try yourFunction(...)
}
try updateUserMemebership(yourFunction: sendNewInvitation(...))
Note: this example is here just to illustrate the usage of rethrows. There are likely better ways to structure your code.
I'm trying to map over Swift's Result type to produce a new Result of a different type but it isn't type checking, and is throwing the error:
error: cannot convert value of type Result<ParseSuccess<B>, ParseError> to closure result type Result<Success, Failure>
I've tried using a switch on the result and matching on the .success and .failure but I get a similar type checking error. I've also tried specifying the input and return types explicitly with no luck.
I've replicated the code in other languages I'm a little more familiar with (F# and Kotlin) and it seems to type check there.
The swift compiler shows a sqiggly under this line:
return self.apply(input).map { result in ParseSuccess(transform(result.data), result.input) }
Where am I going wrong with this code?
import Foundation
class ParseError : Error {
let err: String
init(_ err: String) {
self.err = err
}
}
class ParseSuccess<A> {
let data: A
let input: Substring
init(_ data: A, _ input: Substring) {
self.data = data
self.input = input
}
}
class Parser<A> {
let fn: (Substring) -> Result<ParseSuccess<A>, ParseError>
init(_ fn: #escaping (Substring) -> Result<ParseSuccess<A>, ParseError>) {
self.fn = fn
}
func apply(_ input: Substring) -> Result<ParseSuccess<A>, ParseError> {
return self.fn(input)
}
func map<B>(_ transform: #escaping (A) -> B) -> Parser<B> {
return Parser { (input) in
return self.apply(input).map { result in
ParseSuccess(transform(result.data), result.input) }
}
}
}
For what it's worth, I'm sure this is probably not the best way to start writing a parser combinator, but I've gotten caught up in trying to figure out where I went wrong here!
Thank you
It's a weird behaviour of the unqualified used of Paraser in the line return Parser {. When a generic type is mentioned in its unqualified form, within the body of the generic type's declaration, the type parameters are inferred to be unchanged. For example, a function on Array<T> can return simply Array, and the T is implied to be the same as the value of T for self. Needless to say, I'm not a huge fan of this behaviour.
Changing it to return Parser<B> { ... solves the issue.
Here's how I found the issue. When working with generics errors, I always try to insert a ton of type annotations to "pin down" the data types, to confirm that the compiler and me are on the same page.
I first wrote:
func map<B>(_ transform: #escaping (A) -> B) -> Parser<B> {
return Parser { (input) in
return self.apply(input).map { result in
let newData: ParseSuccess<B> = transform(result.data)
return ParseSuccess(newData, result.input)
}
}
}
At which point the error changed to:
Untitled 2.swift:32:10: error: cannot convert return expression of type 'Parser<A>' to return type 'Parser<B>'
return Parser { (input) in
^~~~~~~~~~~~~~~~~~~~
I am trying to define a Swift closure with two arguments but it is throwing compilation errors. What am I doing wrong?
var processor: (CMSampleBuffer, CVPixelBuffer) throws -> Void { (sampleBuffer, outputPixelBuffer) in
....
}
EDIT: An = was missing which was correctly pointed out in the comments. But now I am trying to pass this closure as a param and it is giving compilation errors in declaration:
func process(_ processor: ((_ sampleBuffer: CMSampleBuffer, toPixelBuffer:CVPixelBuffer) throws)? = nil) {
}
So the following code seems to pass in a Playground:
func process(_ processor: ((String, String))? = nil) {
}
I am pretty sure the main problem is that you want to force throws as a keyword. I don't think that's possible in any way, I would rather propose to use a Result enum pattern that looks more or less like this:
enum ProcessResult {
case success(someReturnValue: YourType) // Or no associated value if you just want to know it worked
case failed(anError: Error)
}
By requiring the block to return a ProcessResult you can enforce something you might use a try/catch for in other languages.
Function Type needs to be written in this syntax:
( ArgumentList ) throws -> ResultType
(Simplified, you can find a full description in the link above.)
The keyword throws is optional according to your requirement, but -> ResultType is required even if the ResultType is Void.
And ArgumentList cannot have parameter labels, you need to use _ as parameter label when you want to show parameter names for readability.
So, your process(_:) should be something like this:
func process(_ processor: ((_ sampleBuffer: CMSampleBuffer, _ toPixelBuffer: CVPixelBuffer) throws -> Void)? = nil) {
//...
}
Or else, if you define a typealias for the parameter type, you can rewrite it as follows:
typealias ProcessorType = (_ sampleBuffer: CMSampleBuffer, _ toPixelBuffer: CVPixelBuffer) throws -> Void
func process(_ processor: ProcessorType? = nil) {
//...
}
One more, when you ask something about compilation errors, it is strongly recommended to show whole error message.
You can find a copyable text through the Report Navigator in the Navigator pane (in the Left side of Xcode).
I am following John Sundell's post (https://www.swiftbysundell.com/posts/providing-a-unified-swift-error-api) on using a nice perform function to create a unified error API
func perform(_ expression: #autoclosure () throws -> T,
orThrow error: Error) throws -> T {
do {
return try expression()
} catch {
throw error
}
}
func someFunction(url: URL) throws -> Data {
...
return try perform(Data(contentsOf: url),
orThrow: SearchError.dataLoadingFailed(url))
}
But it gives me Use of undeclared type error at T. Isn't Xcode supposed to figure out type T by looking at the return type of the function passed in?
You need to declare any type parameters used in your function signature:
func perform<T>(
_ expression: #autoclosure () throws -> T,
orThrow error: Error
) throws -> T { ...