Throwing an error from a stored throwing function - swift

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))
}

Related

Generic type not preserved when called within another generic function

[Updated with a less contrived example]
I'm trying to extend a generic function to provide different behavior for a specific type. This works as expected when I call the function directly. But If I call it from within another generic function, the original generic type is not preserved and I get the default behavior. I'm a bit new to Swift, so I may be missing something obvious here.
My code looks something like this:
protocol Task {
associatedtype Result
}
struct SpecificTask: Task {
typealias Result = String
// other taks related properties
}
enum TaskRunner<T: Task> {
static func run(task: T) throws -> T.Result {
// Task not supported
throw SomeError.error
}
}
extension TaskRunner where T == SpecificTask {
static func run(task: T) throws -> T.Result {
// execute a SpecificTask
return "Some Result"
}
}
func run<T: Task>(task: T) throws -> T.Result {
// Additional logic related to running the task
return try TaskRunner.run(task: task)
}
print(try TaskRunner.run(task: SpecificTask())) // Prints "Some Result"
print(try run(task: SpecificTask())) // Throws SomeError
I need the top-level run function to call the SpecificTask version of the lower-level run() function, but the generic version of the function is called instead
You're trying to reinvent class inheritance with generics. That is not what generics are for, and they don't work that way. Generic methods are statically dispatched, which means that the code is chosen at compile-time, not runtime. An overload should never change the behavior of the function (which is what you're trying to do here). Overrides in where clauses can be used to improve performance, but they cannot be used to create dynamic (runtime) dispatch.
If you must use inheritance, then you must use classes. That said, the problem you've described is better solved with a generic Task rather than a protocol. For example:
struct Task<Result> {
let execute: () throws -> Result
}
enum TaskRunner {
static func run<Result>(task: Task<Result>) throws -> Result {
try task.execute()
}
}
let specificTask = Task(execute: { "Some Result" })
print(try TaskRunner.run(task: specificTask)) // Prints "Some Result"
Notice how this eliminates the "task not supported" case. Rather than being a runtime error, it is now a compile-time error. You can no longer call this incorrectly, so you don't have to check for that case.
If you really want dynamic dispatch, it is possible, but you must implement it as dynamic dispatch, not overloads.
enum TaskRunner<T: Task> {
static func run(task: T) throws -> T.Result {
switch task {
case is SpecificTask:
// execute a SpecificTask
return "Some Result" as! T.Result // <=== This is very fragile
default:
throw SomeError.error
}
}
}
This is fragile because of the as! T.Result. If you change the result type of SpecificTask to something other than String, it'll crash. But the important point is the case is SpecificTask, which is determined at runtime (dynamic dispatch). If you need task, and I assume you do, you'd swap that with if let task = task as? SpecificTask.
Before going down that road, I'd reconsider the design and see how this will really be called. Since the Result type is generic, you can't call arbitrary Tasks in a loop (since all the return values have to match). So it makes me wonder what kind of code can actually call run.

What is the Never return type?

What does a func with return type Never do?
For example:
func addNums() -> Never {
//my code
}
What will be the difference if I kept the return type as Void like this?
func addNums() -> Void {
//my code
}
Suppose I wish to handle a fatalError (as said by dpassage); the below code will be sufficient:
print("its an error")
return
Apple documentation says:
The return type of functions that do not return normally, that is, a type with no values.
Source: Developer
This was not a duplicate question of When and how to use #noreturn attribute in Swift?, as I wish for a more detailed answer which needs details like:
Practical examples on the difference between both Never and Void as return types
Condition by which we should adopt these return types.
Also there is a chance the return type can be nil; I need a comparison of that feature too
The answer should focus on the differences.
Never return type was introduced in Swift 3 to substitute #noreturn key.
See justification in this proposal:
SE-0102 Remove #noreturn attribute and introduce an empty Never type
As official documentation explains:
The return type of functions that do not return normally; a type with
no values.
Use Never as the return type when declaring a closure,
function, or method that unconditionally throws an error, traps, or
otherwise does not terminate.
Source: https://developer.apple.com/documentation/swift/never
Basic illustration:
// The following function is our custom function we would use
// to manually and purposefully trigger crash. In the logs,
// we can specify what exactly went wrong: e.g. couldn't cast something,
// couldn't call something or some value doesn't exist:
func crashApp() -> Never {
fatalError("Something very, very bad happened! Crash the app!")
}
Usage specifics and advantages over #noreturn, as referenced by Erica Sadun:
Never allows a function or method to throw: e.g. () throws -> Never. Throwing allows a secondary path for error remediation, even in functions that were not expected to return.
As a first class type, Never works with generics in a way that the #noreturn attribute could not.
Never proactively prevents a function from claiming both a return type and no-return at the same time. This was a potential issue under the old system.
First note (regarding secondary error remediation) is probably particularly important. Never function can have complex logic and throw – not necessarily crash.
Let's see some interesting use cases and comparison between Never and Void
Never
Example 1
func noReturn() -> Never {
fatalError() // fatalError also returns Never, so no need to `return`
}
func pickPositiveNumber(below limit: Int) -> Int {
guard limit >= 1 else {
noReturn()
// No need to exit guarded scope after noReturn
}
return rand(limit)
}
Example 2
func foo() {
abort()
print("Should not reach here") // Warning for this line
}
Example 3
func bar() -> Int {
if true {
abort() // No warning and no compiler error, because abort() terminates it.
} else {
return 1
}
}
abort() is defined as:
public func abort() -> Never
Void
These examples would not have been possible with it returning Void:
public func abortVoid() -> Void {
fatalError()
}
func bar() -> Int {
if true {
abortVoid() // ERROR: Missing return in a function expected to return 'Int'
} else {
return 1
}
}
And to pack it up with abort() returning Never:
func bar() -> Int {
if true {
abort() // No ERROR, but compiler sees it returns Never and warns:
return 2 // Will never be executed
} else {
return 1
}
}
We use Void to tell compiler there is no return value. Application keeps running.
We use Never to tell compiler there is no return to caller site. Application runloop is terminated.
Void
Void is itself a return type which is a tuple with zero elements. You can use Void and () interchangeably.
Look at these examples,
func yourFunc() {} This is a function without a return type, which basically returns a tuple with zero elements, that can be written as ()
func yourFunc() -> Void {} Function which is explicitly informing the compiler about return type of void
func yourFunc() -> () {} This return type of () displays the same as void type. () indicates a tuple with zero elements
Never
Never return-type informs the compiler that no need exists to return an empty tuple (). Also, function with the never return type is used for the exit point of the current execution like a crash, fatal error, abort or exit.
For a detailed understanding of never, let's have a look at an abort() example :
1.
func yourFunc() {
abort()
print("Will not reach at this point") //Warning for this line
}
2.
func yourFunc() -> Int {
if true {
abort()
} else {
return 1
}
}
From the above code snippets, we can see when we call abort() (which doesn't return a value) as the last statement in a function that expects a value to be returned (in our case Int). The compiler doesn't generate a warning.
abort()
public func abort() -> Never
Similarly for exit():
public func exit(_: Int32) -> Never
The apple documentation says: "Use Never as the return type when declaring a closure, function, or method that unconditionally throws an error, traps, or otherwise does not terminate."
So if you want to write a custom function that logs a catastrophic error, you should use the return type Never to signal to the compiler:
func catastrophicErrorDisplay(error: String) -> Never {
DisplaySomeCustomLogFacility(error)
}
In short "Never is used for sudden and total failure from which recovery is impossible."
To better understand Never and Void, and how Never is useful in more contexts than the old #noreturn was, let's first look at what the two types actually are defined as:
Never is defined here as:
public enum Never {}
Since there is no way to instantiate a value of an empty enum, the type system guarantees that no instance of Never can exist. This means functions that specify their return type as Never are prevented by the type system from actually returning under any circumstances.
The compiler takes this into account when doing control-flow analysis. For example, these two functions both compile without error, whereas they would fail if a function that returns Void was substituted for fatalError:
func foo(fail: Bool) -> String {
if fail {
fatalError()
} else {
return "foo"
}
// notice there is no return statement here
}
func bar(fail: Bool) -> Void {
let s: String
if fail {
fatalError()
// the compiler doesn't complain s is not initialized here
} else {
s = "bar"
}
print(s)
}
Void is defined here as:
public typealias Void = ()
There are no two different instances of an empty tuple. Thus, the return value of functions returning Void holds no information.
You can actually write return () or return Void(). You can also use the "value" returned, like this:
func empty() -> Void {}
let v = empty()
print(type(of: v)) // prints "()"
although the compiler will warn "Constant 'v' inferred to have type 'Void', which may be unexpected".
Defining both Never and Void in terms of the type system rather than as special language features enables us to do some pretty clever things with generics. Let's look at an example of a Result type, generic over both the success and failure type.
enum Result<R, E> {
case success(R)
case failure(E)
}
A possible specialization of this would be Result<Void, MyError>. This would mean you have a result that, on success, does not hold any information beyond the fact it succeeded.
Another possibility could be Result<String, Never>. This result is guaranteed by the compiler to never be the failure case.
Optionals interact with Never and Void in a similar way. Never? can only ever be nil, and Void? only holds the information wether it is nil or not, nothing more (it's basically a more complicated Bool). Both of these are not very useful on their own, but might appear when Never or Void are used as generic parameters somewhere.
In practice, you will rarely write functions returning Never. I have personally used it to wrap fatalError to create a function I use to mark functions that are not implemented yet:
func unimplemented(f: String = #function) -> Never {
fatalError("\(f) is not implemented yet")
}
Another example of a function returning Never is dispatchMain(), which can be used in command-line utilities to start the DispatchQueue.main. Since this queue then waits for new blocks, dispatchMain() never returns.
Never indicates that the function will never return. It's intended to be used for things like fatalError which cause your program to crash intentionally, often after logging an error. You probably shouldn't use it unless you're doing something like making a handler for catastrophic errors in your application.
This is different from a function which just doesn't return a value, as in your second snippet. You could also write that as func addNums() -> Void.

Does throwing a asynchronous function make it synchronous in Swift?

This is a code example taken from Perfect Swift PostgresSTORM library.
do{
//Create a user object
let obj = User()
obj.name = "someUser"
//Save it to db
try obj.save({ id in
print(2..)
obj.id = id as! Int
})
print("1..")
}catch{
print("Something went wrong.")
}
//Go to next page
print("3..")
I expected to see the print logs to be
1..
3..
2..
but, The logs looked like this.
2..
1..
3..
It's highly unlikely for "2.." to print before "1..". Is it the "try" that's making it to run as a synchronous function?
It's totally up to PostgresSTORM implementation of save. I'm not familiar with it so I can't say whether it's actually asynchronous or not but I can offer you two dummy implementations, one asynchronous and one synchronous of a method with a similar signature.
Asynchronous:
func save(callback: #escaping (Int) -> Void) throws {
OperationQueue.main.addOperation {
callback(0)
}
}
Synchronous:
func save(callback: (Int) -> Void) throws {
callback(0)
}
(Note that it doesn't throw any exception in this example just for simplicity's sake).
try is required by Swift compiler when you call a function that might throw an exception and has no impact in (a)synchronous execution of the method. In fact try is just required to ensure that when we use the method we are well aware about it possibly throwing an exception.
I might be wrong but if this is SwiftORM's implementation of save method then callback is always synchronously called.

What are the differences between throws and rethrows in Swift?

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.

Purpose of try! in Swift

I am confused as what exactly is the purpose of try!. The documentation says that you can use try! to call methods that can throw exception but try! is used as a promise from the caller that it will not throw an exception or the developer is not interested in the result of the exception.
So, if the developer is not interested in catching that exception then why even throw the exception from the method in the first case.
func foo() throws {
}
try! foo()
Why not:
func foo() {
}
foo()
struct Error:ErrorType{}
// function is defined somewhere
// i know just the declaration
//
// func foo(i:Int!) throws -> Void
//
func foo(i: Int!) throws {
if i == nil {
throw Error()
}
}
// i am sure the call is 'safe'
// this simplifies my code
try! foo(2)
//foo(2) // error: call can throw but is not marked with 'try'
let str = "2,2" // mimic result of some operation
do {
// i am not sure if str represents an Int
try foo(Int(str))
} catch {
print("error")
}
A function could throw errors if it gets illegal parameters. However, if this function is used with an UI that allows only the input of legal parameters, then you can call this function with try!, because you are sure that the exception is never thrown.
I read a book on Swift that says you can use try! in the early stages of development, because the app will crash if an error is thrown, so a developer or tester cannot overlook this error. However, I wouldn't use this, because it could be forgotten to remove a try! before the code goes into production.