Do you need help on a specific subject? Use the contact form (Request a blog entry) on the right hand side.

2015-04-27

Functions returning a value or an error: use a tuple or an enum?

Many functions will either return a value or an error. Sometimes the error can simply be returned as a nil value. In that case the solution is simple: use an optional value as the return value.

But sometimes the function needs to return information about the error that occurred. Mostly to inform the user of some unexpected situation. In that case an optional value is of little use: the function needs to return either the successful value or the error information.

I am currently torn between two possible solutions for this: one is to return a tuple, like this:

func readFile(path: String) -> (result: String?, errorMessage: String?) {...}

the other is to introduce an enum to distinguish between the possible values like this:

enum ReadFileResult {
    case Success(String)
    case Error(String)
}

func readFile(path: String) -> ReadFileResult {...}

A full example using tuples:


func readFile(path: String) -> (result: String?, errorMessage: String?) {
    var error: NSError?
    if let result = String(contentsOfFile: path, encoding: NSUTF8StringEncoding, error: &error) {
        return (result, nil)
    } else {
        let errorMessage = error?.localizedDescription ?? "File read error without localized description"
        return (nil, errorMessage)
    }
}

let (fileContent, errorMessage) = readFile("/Users/Rien/MyFile.txt")

if let content = fileContent {
    // do stuff with file contents like:
    println(content)
} else if let message = errorMessage {
    // do stuff with error message like:
    println(message)
} else {
    fatalError("Impossible")
}

rather short and sweet imo.

A full example using an enum:


enum ReadFileResult {
    case Success(String)
    case Error(String)
}

func readFile(path: String) -> ReadFileResult {
    var error: NSError?
    if let result = String(contentsOfFile: path, encoding: NSUTF8StringEncoding, error: &error) {
        return ReadFileResult.Success(result)
    } else if let message = error?.localizedDescription {
        return ReadFileResult.Error(message)
    } else {
        return ReadFileResult.Error("File read error without localized description")
    }
}

let result = readFile("/Users/Rien/MyFile.txt")

switch result {
case let .Success(content):
    // do something with content like:
    println(content)
case let .Error(message):
    // do something with error message like:
    println(message)
}

this is perhaps a bit easier to read, but more verbose as we have to introduce a new type (enum). In my mind there is no clear winner. The first one is simpler to implement, but needs the fatalError handling, while the second one is more verbose but avoids the fatalError.

In an API, I would use the enum solution, but when implementing something in-situ I would probably go for the tuple.

Happy coding...

Did this help?, then please help out a small independent.
If you decide that you want to make a small donation, you can do so by clicking this
link: a cup of coffee ($2) or use the popup on the right hand side for different amounts.
Payments will be processed by PayPal, receiver will be sales at balancingrock dot nl
Bitcoins will be gladly accepted at: 1GacSREBxPy1yskLMc9de2nofNv2SNdwqH

We don't get the world we wish for... we get the world we pay for.

No comments:

Post a Comment