List of cases in Go that allow omitting an optional second returned value - go

When calling functions or methods with multiple return values, the caller, if unpacking any return values, must unpack them ALL.
f, err := os.Open("filename")
The err variable must be either declared or ignored with the blank identifier _, but it cannot be omitted.
However, there are some operations built into the language, which allow one to omit the optional second return value.
Is there a more or less formal list of situations where an operation with a data structure or a function call returns a second value that is optional and can be ignored by only receiving the first return value? For example:
m := make(map[string]int)
v, ok := m["hello"]
The ok variable is entirely optional and can be omitted.
v := m["hello"]
Likewise, with channels:
v, ok := <-ch
or
v := <-ch
Are there any other cases of that behavior beyond the two above?

The expressions with optional values when used in an assignment or initialization are: type assertion, map index and channel receive.
The specification does not have a formal list of these expressions, but the expressions are all described in the specification.

Related

Parallel table - driven testing in go fails miserably

I have the following test function
func TestIntegrationAppsWithProductionSelf(t *testing.T) {
// here is where the apps array that will act as my test suite is being populated
myapps, err := RetrieveApps(fs)
for _, v := range apps {
v := v
t.Run("", func(t *testing.T) {
t.Parallel()
expectedOutput = `=` + v + `
`
cmpOpts.SingleApp = v
t.Logf("\t\tTesting %s\n", v)
buf, err := VarsCmp(output, cmpOpts)
if err != nil {
t.Fatalf("ERROR executing var comparison for %s: %s\n", v, err)
}
assert.Equal(t, expectedOutput, buf.String())
})
}
}
The test fails, despite the fact that when I remove t.Parallel() (even keeping the sub-testing structure) it succeeds.
The failure (happens as said before only when t.Parallel() is incorporated) has to do with the fact that the values to be compared passed to assert are out of sync, i.e. the assert method compares values that it shouldn't)
Why is that?
I also perform this cryptic re-assignment of the test suite variable (v := v) which I do not understand)
edit: Wandering if it was the usage of the assert method from this package, I made the following substitution, nonetheless the end result is the same,
//assert.Equal(t, expectedOutput, buf.String())
if expectedOutput != buf.String() {
t.Errorf("Failed! Expected %s - Actual: %s\n", expectedOutput, buf.String())
}
Let's dissect the case.
First, let's refer to the docs on testing.T.Run:
Run runs f as a subtest of t called name.
It runs f in a separate goroutine <…>
(Emphasis mine.)
So, when you call t.Run("some_name", someFn), that SomeFn is being run by the test suite as if you would manually do something like
go someFn(t)
Next, let's notice that you do not pass a named function into your call to t.Run, but rather you pass it a so-called function literal; let's cite the language spec on them:
Function literals are closures: they may refer to variables defined in a surrounding function. Those variables are then shared between the surrounding function and the function literal, and they survive as long as they are accessible.
In your case, it means when the compiler compiles the body of your function literal, it makes the function "close over" any variable its body mentions, and which is not one of the formal function parameters; in your case, the only function parameter is t *testing.T, hence every other accessed variable is captured by the created closure.
In Go, when a function literal closes over a variable, it does so by retaining a reference to that variable — which is explicitly mentioned in the spec as («Those variables are then shared between the surrounding function and the function literal <…>», again, emphasis mine.)
Now notice that loops in Go reuse iteration variables on each iteration; that is, when you write
for _, v := range apps {
that variable v is created once in the "outer" scope of the loop and then gets reassigned on each iteration of the loop. To recap: the same variable, whose storage is located at some fixed point in memory, gets assigned a new value on each iteration.
Now, since a function literal closes over external variables by keeping references to them — as opposed to copying their values at the "time" of its definition into itself, — without that funky-looking v := v "trick" each function literal created at each call to t.Run in your loop would reference exactly the same iteration variable v of the loop.
The v := v construct declares another variable named v which is local to the loop's body and at the same time assigns it the value of the loop iteration variable v. Since the local v "shadows" loop iterator's v, the function literal declared afterwards would close over that local variable, and hence each function literal created on each iteration will close over a distinct, separate variable v.
Why is this needed, you may ask?
This is needed because of a subtle problem with the interaction of loop iteration variable and goroutines, which is detailed on the Go wiki:
when one does something like
for _, v := range apps {
go func() {
// use v
}()
}
A function literal closing over v is created, and then it's run with the go statement—in parallel both with the goroutine which runs the loop and with all the other goroutines started on the len(apps)-1 other iterations.
These goroutines running our function literals all refer to the same v and so they all have a data race over that variable: the goroutine running the looop writes to it, and the goroutines running function literals read from it—concurrently and without any synchronization.
I hope, by now you should see the puzzle's pieces coming together: in the code
for _, v := range apps {
v := v
t.Run("", func(t *testing.T) {
expectedOutput = `=` + v + `
// ...
the function literal passed to t.Run closes over v, expectedOutput,
cmpOpts.SingleApp (and may be something else),
and then t.Run() makes that function literal run in a separate goroutine, as documented,—producing the classic data race on expectedOutput and cmpOpts.SingleApp, and whatever else which is not v (a fresh variable on each iteration) or t (passed to the call of the function literal).
You might run go test -race -run=TestIntegrationAppsWithProductionSelf ./... to see the engaged race detector crashing your test case's code.
I am going to post what actually worked, but (unless the question is closed) I will accept the answer that actually elaborates on it.
The problem was that the variable used to store the expectedOutput was declared with a declaration inside the TestIntegrationAppsWithProductionSelf function but outside the for loop (this is now reflected in the code snippet of the initial question).
What worked was to remove the var expectedOutput string statement and do within the for loop
for _, v := range apps {
v := v
expectedOutput := `=` + v + `
`
t.Run("", func(t *testing.T) {
t.Parallel()
cmpOpts.SingleApp = v
t.Logf("\t\tTesting %s\n", v)
buf, err := VarsCmp(output, cmpOpts)
if err != nil {
t.Fatalf("ERROR executing var comparison for %s: %s\n", v, err)
}
//assert.Equal(t, expectedOutput, buf.String())
if expectedOutput != buf.String() {
t.Errorf("Failed! Expected %s - Actual: %s\n", expectedOutput, buf.String())
}
})
}

Appending to a slice using reflection

I would like to append to a slice using only reflection. But I can't figure out how to "replace" the value of a with the new slice.
func main() {
fmt.Println("Hello, playground")
a := []string {"a","b","c"}
values := []string {"d","e"}
v := reflect.ValueOf(a)
fmt.Printf("%t\n\n", v.Type())
fmt.Printf("%t\n\n", v.Type().Elem().Kind())
for _, val := range values {
v.Set(reflect.Append(v, reflect.ValueOf(val)))
}
fmt.Printf("%t - %v", a, a)
}
This code is available for fiddling at https://play.golang.org/p/cDlyH3jBDS.
You can't modify the value wrapped in reflect.Value if it originates from a non-pointer. If it would be allowed, you could only modify a copy and would cause more confusion. A slice value is a header containing a pointer to a backing array, a length and a capacity. When you pass a to reflect.ValueOf(), a copy of this header is made and passed, and any modification you could do on it could only modify this header-copy. Adding elements (and thus changing its length and potentially the pointer and capacity) would not be observed by the original slice header, the original would still point to the same array, and would still contain the same length and capacity values. For details see Are Golang function parameter passed as copy-on-write?; and Golang passing arrays to the function and modifying it.
You have to start from a pointer, and you may use Value.Elem() to obtain the reflect.Value descriptor of the pointed, dereferenced value. But you must start from a pointer.
Changing this single line in your code makes it work:
v := reflect.ValueOf(&a).Elem()
And also to print the type of a value, use the %T verb (%t is for bool values):
fmt.Printf("%T\n\n", v.Type())
fmt.Printf("%T\n\n", v.Type().Elem().Kind())
// ...
fmt.Printf("%T - %v", a, a)
Output (try it on the Go Playground):
Hello, playground
*reflect.rtype
reflect.Kind
[]string - [a b c d e]
For a deeper understanding of Go's reflection, read the blog post: The Laws of Reflection
And read related questions+answers:
Assigning a value to struct member through reflection in Go
Changing pointer type and value under interface with reflection
Using reflection SetString

How to understand the golang multi-return value

Golang supports assigning multiple return values to multiple left hand side variables. E.g:
func test() (string, string) {
return "1", "1"
}
a, b := test()
or
a, _ := test()
and the number of receiving variables and return values must match:
b = test() //wrong
But for some built-in types, such as [] or <-, a variable number of return values are supported
key, exist := map[key]
key := map[key]
I can read value from channel like this
c <- myChan
c, exist <- myChan
How can we explain the inconsistency? Is this a capability reserved to the core go runtime/language?
This behavior clearly specified in the golang specification:
Receive operator
A receive expression used in an assignment or initialization of the special form
x, ok = <-ch
x, ok := <-ch
var x, ok = <-ch
var x, ok T = <-ch
yields an additional untyped boolean result reporting whether the communication succeeded. The value of ok is true if the value received was delivered by a successful send operation to the channel, or false if it is a zero value generated because the channel is closed and empty.
Index expression
An index expression on a map a of type map[K]V used in an assignment or initialization of the special form
v, ok = a[x]
v, ok := a[x]
var v, ok = a[x]
var v, ok T = a[x]
yields an additional untyped boolean value. The value of ok is true if the key x is present in the map, and false otherwise.
Assignments
A tuple assignment assigns the individual elements of a multi-valued operation to a list of variables. There are two forms. In the first, the right hand operand is a single multi-valued expression such as a function call, a channel or map operation, or a type assertion. The number of operands on the left hand side must match the number of values. For instance, if f is a function returning two values,
x, y = f()
assigns the first value to x and the second to y. In the second form, the number of operands on the left must equal the number of expressions on the right, each of which must be single-valued, and the nth expression on the right is assigned to the nth operand on the left.
Therefore as you can see this behavior being specified by language design and you cannot achieve those specified for Receive operator and Index expression by yourself.
You are confusing multiple values returned from a function with the so-called "comma ok" idiom.
With a function's return values you must either handle all of them, or none of them. Simple.
"Comma ok" is more subtle. Often the behavior is changed if you have a second value specified. Consider these statements:
v, ok := x.(int)
// vs
v := x.(int)
If x is an interface holding an integer, all is well, however, if x holds a different type the first statement will work (returning 0, false), and the second one will panic.
Each type of statement with a "comma ok" form is a different special case, and they are not the same as other kinds of multiple assignments (such as multiple function return values). You cannot compare them, each is its own thing, with its own rules.

Errors in Golang - assessing errors

I am trying to understand the following example
https://gobyexample.com/errors
I understand most of it except for this part:
_, e := f2(42)
if ae, ok := e.(*argError); ok {
fmt.Println(ae.arg)
fmt.Println(ae.prob)
}
I'm not sure what this line does :
if ae, ok := e.(*argError); ok {
e.(*argError)
is a type assertion that casts the value e to *argError type. This is the type f2() returns on error - it is a pointer to an argError struct, which implements the error interface. This type assertion evaluates multi-valued to (ae,ok), where ae is the *argError typed value, if successful, and ok is a boolean letting you know if it was successful.
if statements in go can be separated into an initial assignment part, then a semicolon, then a boolean condition to evaluate to determine the branch.
Altogether, then,
if ae, ok := e.(*argError); ok {
means: try to cast e to *argError, if that is successful (do if block).
Why do this? Because argError has fields that are not in a plain error (arg, prob), and you might want to use those. In real code where you do this, you'd likely also need to handle the case where e was not an argError, but some other error type, in an "else" branch.
Add Go official link here on 'Type assertions':
A type assertion provides access to an interface value's underlying concrete value.
t := i.(T)
This statement asserts that the interface value i holds the concrete type T and assigns the underlying T value to the variable t.
If i does not hold a T, the statement will trigger a panic.
To test whether an interface value holds a specific type, a type assertion can return two values: the underlying value and a boolean value that reports whether the assertion succeeded.
t, ok := i.(T)
If i holds a T, then t will be the underlying value and ok will be true.
If not, ok will be false and t will be the zero value of type T, and no panic occurs.
Note the similarity between this syntax and that of reading from a map.

multiple-value in single-value context (go)

The code below returns the error multiple-value in single-value context. I fail to understand why because it can't be more clear that the function has one argument ( I pass an empty string ""), and returns a string and an error (I assign it to r and err).
package main
import "fmt"
type Some struct{}
func main() {
cl := Some{}
r, err := &cl.Start("")
fmt.Println(r)
}
func (cs *Some) Start(sg string) (string, error) {
return sg, nil
}
It's an operator precedence thing. &cl.Start() is the same as &(cl.Start()), which isn't what you want.
Use parentheses to clarify that what you want is (&cl).Start().
Or you could just use a pointer variable.
func main() {
cl := &Some{}
r, err := cl.Start("")
fmt.Println(r)
}
As already stated in the comment, remove the & from &cl.Start("") and it will work - though you'l then get an error about err being declared and not used.
Or you can write it as (&cl).Start("") and that too will work.
The explanation is here:
The rule about pointers vs. values for receivers is that value methods can be invoked on pointers and values, but pointer methods can only be invoked on pointers.
This rule arises because pointer methods can modify the receiver;
invoking them on a value would cause the method to receive a copy of
the value, so any modifications would be discarded. The language
therefore disallows this mistake. There is a handy exception, though.
When the value is addressable, the language takes care of the common
case of invoking a pointer method on a value by inserting the address
operator automatically.
and also here:
A method call x.m() is valid if the method set of (the type of) x contains m and the argument list can be assigned to the parameter list of m. If x is addressable and &x's method set contains m, x.m() is shorthand for (&x).m()

Resources