Return error from the channel

You can pass in an error channel as well as a result channel.

errors := make(chan error, 0)
results := make(chan string, 0)

password := "test"

go func() {
    result, err := createHashedPassword(string password)
    if err != nil {
        errors <- err
        return
    }

    results <- result
}()

// do something else

// When you are ready to read from goroutine do this:
select {
    case err := <- errors:
        println(err)
    case res := <- results:
        println(res)
}

It's common to bundle multiple outputs into a struct, and return them together over a single channel.

type Result struct {
    Message string
    Error error
}

ch := make(chan Result)

Here are my two preferred ways:

Two channels, wrapped

This is the "two channels" way, but wrapped into a function to make it look similar to the common pattern:

func createHashedPasswordAsynchronously(password string) (chan string, chan error) {
    resultCh := make(chan string)
    errorCh := make(chan error)

    go func(password string) {
        //code
        if err != nil {
            errorCh <- errors.New("Does not compute")
        } else {
            resultCh <- "8badf00d"
        }
    }(password)

    return resultCh, errorCh
}

And called like this:

resultCh, errorCh := createHashedPasswordAsynchronously("mysecret")
select {
case result := <-resultCh:
    storeHashedPassword(result)
case err := <-errorCh:
    log.Println(err.Error())
}

Anonymous struct

This is the "anonymous struct" way, similar to @saward's answer, but without naming the struct members explicitly:

go func(password string, ch chan struct {
    string
    error
}) {
    //code
    if err != nil {
        ch <- struct {
            string
            error
        }{"", errors.New("Does not compute")}
    } else {
        ch <- struct {
            string
            error
        }{"8badf00d", nil}
    }
}("mysecret", ch)

r := <-ch
if r.error != nil {
    log.Println(r.error.Error())
} else {
    storeHashedPassword(r.string)
}

(since I cannot comment yet...)

I echo what JimB said with:

type Result struct {
    Message string
    Error error
}

ch := make(chan Result)

The trouble with two separate channels, one for the result, and another for the error, is that (as I understand) it won't support concurrent threads out of the box.

You could, for example, have two threads sending data at the same time, where the replies get out of order. That is, you receive the result from thread 1 first, but the error from thread 2 first.

It's easy to create new types like JimB suggested, and should work well with goroutines.

Tags:

Go