Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create chan for func with two return args

Is it posible to create channel for function boolInt without redefining it?

package main

func boolInt() (bool, int) {
    return false, 1
}

func main() {
    chanBool := make(chan bool)
    chanInt := make(chan int)
    go func() {
        // chanBool <- boolInt() // error: multiple-value boolInt() in single-value context
        chanBool, chanInt <- boolInt() //  syntax error: unexpected semicolon or newline, expecting := or = or comma
    }()

}

When I try to use it in single value context chanBool <- boolInt() I get an error: multiple-value boolInt() in single-value context.

In 2-value context: chanBool, chanInt <- boolInt() got error: syntax error: unexpected semicolon or newline, expecting := or = or comma.

playground

like image 204
Maxim Yefremov Avatar asked Feb 11 '23 05:02

Maxim Yefremov


2 Answers

Using 2 different channels

You want to send values to 2 different channels. Sending a value to a channel is not an assignment, so you can't send on both channels in one step.

First store the values returned by boolInt(), then send the values to the 2 channels like this:

go func() {
    b, i := boolInt()
    chanBool <- b
    chanInt <- i
}()

Testing it:

go func() {
    b, i := boolInt()
    chanBool <- b
    chanInt <- i
}()

fmt.Println("Received bool:", <-chanBool)
fmt.Println("Received int:", <-chanInt)

Output:

Received bool: false
Received int: 1

Note: you first have to receive from chanBool because you created unbuffered channels, and because in the example we first send to chanBool, that blocks until the sent value is recevied and only then proceeds to send a value to the chanInt. Attempting to receive from chanInt first would cause a deadlock ("fatal error: all goroutines are asleep - deadlock!").

Solution using only one channel

If you want to send multiple values on a channel, you can create a wrapper struct for the values:

type MyStruct struct {
    b bool
    i int
}

And using it:

ch := make(chan MyStruct)
go func() {
    b, i := boolInt()
    ch <- MyStruct{b, i}
}()

fmt.Println("Received value:", <-ch)

Output:

Received value: {false 1}

Note: You could also use a []interface{} slice as a wrapper, but a struct provides a clearer way and type safety to its fields.

Note #2: It would make things easier and clearer if the boolInt() function itself would return a MyStruct value:

func boolInt() MyStruct {
    return MyStruct{false, 1}
}

In which case code would be as simple as this:

ch := make(chan MyStruct)
go func() {
    ch <- boolInt()
}()

Alternative 1 channel solution

Another option is to make the channel type interface{} so it can receive values of any type, and just send/receive multiple values on it:

ch := make(chan interface{})
go func() {
    b, i := boolInt()
    ch <- b
    ch <- i
}()

fmt.Println("Received values:", <-ch, <-ch)

Output:

Received values: false 1
like image 172
icza Avatar answered Feb 13 '23 22:02

icza


If you want to send two values down a channel together, one option is to use a struct to pack both values together. For example:

type BoolPlusInt struct {
    B bool
    I int
}

You can then create a value of this type holding the two values before sending it down the channel. For instance:

c := make(chan BoolPlusInt)
go func() {
    var v BoolPlusInt
    v.B, v.I = boolInt()
    c <- v
}()

This solution might be preferable if you have multiple goroutines sending or receiving on the channel. Since the two values are packed together you don't need to worry about bugs in one place leading to the two channels getting out of sync.

You can experiment with this suggestion here: http://play.golang.org/p/I_Apg4ciFI

like image 26
James Henstridge Avatar answered Feb 13 '23 22:02

James Henstridge