Nerve
Nerve

Reputation: 6871

Catching return values from goroutines

The below code gives compilation error saying 'unexpected go':

x := go doSomething(arg)

func doSomething(arg int) int{
    ...
    return my_int_value
}

I know, I can fetch the return value if I call the function normally i.e. without using goroutine or I can use channels etc.

My question is why is it not possible to fetch a return value like this from a goroutine.

Upvotes: 150

Views: 152724

Answers (6)

xscorp7
xscorp7

Reputation: 311

There are multiple techniques to achieve this:

  1. Communicate through channels - Whatever needs to be returned, Just pass it on through a channel.

  2. Passing Pointer - Pass a pointer to the results variable to the coroutine.

  3. Define and call your goroutine function inside the parent function:

func MyFunction(arg string) []string {
    result := make([]string, 0)
    var wg sync.WaitGroup
    wg.Add(1)
    go func(input string) {
        defer wg.Done()
        // Add the output to result variable
    }(input)
    wg.Wait()
    return result
}

Upvotes: -1

arviman
arviman

Reputation: 5255

Why not use a channel to write into?

chanRes := make(chan int, 1)
go doSomething(arg, chanRes)
//blocks here or you can use some other sync mechanism (do something else) and wait
x := <- chanRes
func doSomething(arg int, out chan<- int){
    ...
    out <- my_int_value
}

Upvotes: 6

Bharat Khatri
Bharat Khatri

Reputation: 1407

It's a design choice by Go creators. There's a whole lot of abstractions/APIs to represent the value of async I/O operations - promise, future, async/await, callback, observable, etc. These abstractions/APIs are inherently tied to the unit of scheduling - coroutines - and these abstractions/APIs dictate how coroutines (or more precisely the return value of async I/O represented by them) can be composed.

Go chose message passing (aka channels) as the abstraction/API to represent the return value of async I/O operations. And of course, goroutines and channels give you a composable tool to implement async I/O operations.

Upvotes: 6

I159
I159

Reputation: 31149

Why is it not possible to fetch a return value from a goroutine assigning it to a variable?

Run goroutine (asynchronously) and fetch return value from function are essentially contradictory actions. When you say go you mean "do it asynchronously" or even simpler: "Go on! Don't wait for the function execution be finished". But when you assign function return value to a variable you are expecting to have this value within the variable. So when you do that x := go doSomething(arg) you are saying: "Go on, don't wait for the function! Wait-wait-wait! I need a returned value be accessible in x var right in the next line below!"

Channels

The most natural way to fetch a value from a goroutine is channels. Channels are the pipes that connect concurrent goroutines. You can send values into channels from one goroutine and receive those values into another goroutine or in a synchronous function. You could easily obtain a value from a goroutine not breaking concurrency using select:

func main() {

    c1 := make(chan string)
    c2 := make(chan string)

    go func() {
        time.Sleep(time.Second * 1)
        c1 <- "one"
    }()
    go func() {
        time.Sleep(time.Second * 2)
        c2 <- "two"
    }()

    for i := 0; i < 2; i++ {
        // Await both of these values
        // simultaneously, printing each one as it arrives.
        select {
        case msg1 := <-c1:
            fmt.Println("received", msg1)
        case msg2 := <-c2:
            fmt.Println("received", msg2)
        } 
    }
}

The example is taken from Go By Example

CSP & message-passing

Go is largerly based on CSP theory. The naive description from above could be precisely outlined in terms of CSP (although I believe it is out of scope of the question). I strongly recommend to familiarize yourself with CSP theory at least because it is RAD. These short quotations give a direction of thinking:

As its name suggests, CSP allows the description of systems in terms of component processes that operate independently, and interact with each other solely through message-passing communication.

In computer science, message passing sends a message to a process and relies on the process and the supporting infrastructure to select and invoke the actual code to run. Message passing differs from conventional programming where a process, subroutine, or function is directly invoked by name.

Upvotes: 185

joshlf
joshlf

Reputation: 23577

The strict answer is that you can do that. It's just probably not a good idea. Here's code that would do that:

var x int
go func() {
    x = doSomething()
}()

This will spawn off a new goroutine which will calculate doSomething() and then assign the result to x. The problem is: how are you going to use x from the original goroutine? You probably want to make sure the spawned goroutine is done with it so that you don't have a race condition. But if you want to do that, you'll need a way to communicate with the goroutine, and if you've got a way to do that, why not just use it to send the value back?

Upvotes: 95

Tyler
Tyler

Reputation: 22126

The idea of the go keyword is that you run the doSomething function asynchronously, and continue the current goroutine without waiting for the result, kind of like executing a command in a Bash shell with an '&' after it. If you want to do

x := doSomething(arg)
// Now do something with x

then you need the current goroutine to block until doSomething finishes. So why not just call doSomething in the current goroutine? There are other options (like, doSomething could post a result to a channel, which the current goroutine receives values from) but simply calling doSomething and assigning the result to a variable is obviously simpler.

Upvotes: 9

Related Questions