Go: transfer var into anonymous function - go

I am having trouble transferring a variable into an anonymous function. Is there a solution?
import "github.com/lxn/walk"
***
var openAction [12]*walk.Action
for i := 0; i < 12; i++ {
openBmp, err := walk.NewBitmapFromFile(_films[i][0])
if err != nil {
log.Printf("Open bitmap for buildBody() :%v\n", err)
}
openAction[i] = walk.NewAction()
openAction[i].SetImage(openBmp)
openAction[i].SetText(_films[i][2])
openAction[i].Triggered().Attach( func(){
exec(i)
})
mw.ToolBar().Actions().Add(openAction[i])
}
exec(i) where i always = 11

for i := 0; i < 12; i++ {
i := i
...
Crazy as it looks, this is something you will see in Go code. It results from the way closures work and the way variables are scoped. Your anonymous function is a closure that captures i. Specifically, it is capturing a variable called i, not the current value of i, and it captures whatever i is in scope. In your original code this is the loop variable, which is the same variable for each iteration of the loop. All of your closures captured the same variable. The addition of i := i declares a new variable on each iteration. Now each closure will capture this new variable, and on each iteration it will be a different variable.
In a little more detail, the scope of the loop variable i is the for statement. This includes the loop block, but since the declaration of the loop variable i is outside of the block, declaring a new variable with the same name inside the block is legal and creates a new variable at that point in the block. The loop variable is then shadowed. Often a variable declared like this goes on the stack, but in this case compiler escape analysis sees that your closure is still referring to this block variable when it goes out of scope at the end of the block, and so the variable is placed on the heap. On each iteration, the block is reentered and a new variable i is placed on the heap.

I think that this will get you what you want:
openAction[i].Triggered().Attach(func(x int) func() {
return func() { exec(x) }
}(i))
The trick is to have your anonymous function return an anonymous function, and each created function will enclose each of the values of i.

You are encountering a quirk of go's for loops. The i variable in the loop is not a new variable for each iteration. because of this all of your closures are closing over the same variable whose value is changing underneath them. When your code runs after the loop all of the functions see the value 11 for the i they closed over.
The solution is to pass the i into a function which then returns another function that closes over the functions arg. This is why Adam Crosslands solution works.

Related

Short format var declaration in for loop

Please see the code below. (A) is not ok in Go and I understand why. But why is (B) ok in Go ?
(A) is not ok: because re-definition of a (no new var in LHS of := )
(B) should be an error too: because re-definition of r (no new var in LHS of := ) as the loop will execute r := with each iteration while r is still in scope.
package main
import "fmt"
func main() {
a := make([]byte, 10)
fmt.Println(a)
a := make([]byte, 10) //not ok and I understand why : (A)
fmt.Println(a)
for i := 0; i < 5; i++ {
r := make([]byte, 10) //ok, but why is this ok? : (B)
fmt.Println(r)
}
}
The key idea behind the short format declaration is that it has to define at least one new variable in the current block. So it fails in the first case because it is attempting to redefine a in the current block without introducing any new variables. The second block works, because r is a new variable declared in the current block.
As describe in go doc specifications about Short_variable_declarations, it is temporary for the scope.
Unlike regular variable declarations, a short variable declaration may redeclare variables provided they were originally declared earlier in the same block (or the parameter lists if the block is the function body) with the same type, and at least one of the non-blank variables is new. As a consequence, redeclaration can only appear in a multi-variable short declaration. Redeclaration does not introduce a new variable; it just assigns a new value to the original.
Short variable declarations may appear only inside functions. In some contexts such as the initializers for "if", "for", or "switch" statements, they can be used to declare local temporary variables.
You can not redeclare same variable with short variable declaration in same scope.
a, a := 1, 2 // illegal: double declaration of a or no new variable if a was declared elsewhere
In your case, r is in loop scope and each iteration r is a new variable. Because for loop repeats execution of a block.
A "for" statement specifies repeated execution of a block
If you need to clarify this please run below loop code and see r's memory addresses for each iterations. It will print five different addresses.
for i := 0; i < 5; i++ {
r := make([]byte, 10) //ok, but why is this ok? : (B)
fmt.Printf("%p\n",r)
}

How can I not reassign a variable in go? [duplicate]

This question already has answers here:
What is "_," (underscore comma) in a Go declaration?
(9 answers)
Closed 2 years ago.
I don't understand how to reassign a variable in a block scope appropriately in go.
package main
import (
"fmt"
"path/filepath"
)
func main() {
base := "/a/b/c"
other := "/a/b/c/d/e"
for base != other {
other, file := filepath.Split(other) // "other declared but not used"
fmt.Println(file)
}
}
I want to use both parts of filepath.Split, so I need :=, since file is not declared yet. I want other to get shorter and shorter, so I reassign the result of filepath.Split, but the go compiler doesn't let me run this code.
Why is this the case, and how am I supposed to do this sort of thing?
The body of the loop is an another block, so using short variable declaration in it will not use the other variable declared before, outside of the loop, but will create a new other variable scoped to the body block. Given that, this second other variable is not used anywhere, because the base != other condition in the loop refers to the outer other variable, hence the compile-time error.
Create the expected second variable first, and use simple assignment instead of short variable declaration:
base := "/a/b/c"
other := "/a/b/c/d/e"
for base != other {
var file string
other, file = filepath.Split(other) // "other declared but not used"
fmt.Println(file)
}
Note that the above code will run into an endless loop because filepath.Split() leaves the trailing slash in other, so in the next iteration filepath.Split() will return the same other (the last dir will not be cut off), and won't ever change again.
To make your code do what you want, you have to cut trailing slashes off, like this:
for base != other {
var file string
other, file = filepath.Split(other) // "other declared but not used"
fmt.Println(file)
if strings.HasSuffix(other, "/") {
other = other[:len(other)-1]
}
}
This will now run and output (try it on the Go Playground):
e
d
Note that the same thing could be achieved with a lot simpler code if you'd use filepath.Base() to get the last part of the path, and filepath.Dir() to get the parent folder, like this:
base := "/a/b/c"
other := "/a/b/c/d/e"
for base != other {
fmt.Println(filepath.Base(other))
other = filepath.Dir(other)
}
This outputs the same, try it on the Go Playground.
See related questions:
Why does initializing just one variable in a definition fail, in golang
Why there are two ways of declaring variables in Go, what's the difference and which to use?

Redeclared variable unused

This code compiles:
func (wc *WordCounter) Write(buf []byte) (int, error) {
for adv, i := 0, 0; i < len(buf); i += adv {
adv, _, _ = bufio.ScanWords(buf[i:], true)
*wc++
}
return len(buf), nil
}
But the following does not compile. Notice the short declaration adv, token, _ := .. where I expected that adv would be redeclared (as opposed to be declared as a new var):
func (wc *WordCounter) Write(buf []byte) (int, error) {
for adv, i := 0, 0; i < len(buf); i += adv {
// error: adv declared and not used
adv, token, _ := bufio.ScanWords(buf[i:], true)
fmt.Println(string(token))
*wc++
}
return len(buf), nil
}
According to the Go spec:
a short variable declaration may redeclare variables provided they
were originally declared earlier in the same block. [...] Redeclaration does not introduce a new variable; it just assigns a new value to the original.
I guess this means that the for statement is a block in and of itself, and that adv therefore is considered to be declared over again (as opposed to being redeclared) in the for body?
This is working as intended. Consider the following:
https://play.golang.org/p/cyJZgM5QYn
package main
import (
"fmt"
)
func main() {
for i := 0; i < 10; i++ {
fmt.Printf("%p", &i)
i := i
fmt.Printf(" | %p\n", &i)
}
}
Variables you declare within the for loop header are defined for the entire for loop. The first i printed in the above has the same address on every single iteration. On the other hand, variables declared inside the loop itself are local only to that iteration of the loop! Note that the second i printed on each line has a unique address, as a new variable is being created on each iteration. This makes the lifetime of the two variables, and thus their scopes, different. Since they exist in separate scopes, the inner scope can (usually inadvertently) shadow the outer scope variable via the short-form declaration.
I guess this means that the for statement is a block in and of itself,
and that adv therefore is considered to be declared over again (as
opposed to being redeclared) in the for body?
Yes, that's correct. Go specs say following about blocks:
A block is a possibly empty sequence of declarations and statements
within matching brace brackets.
Each "if", "for", and "switch" statement is considered to be in its own implicit block.
So you are declaring a new variable in your for-block with the same name as the previously declared variable. The new variable shadows the previously declared variable making it inaccessible from inside the for-block.
This is a common source of bugs if you don't realize there are now two variables with the same name and you think you are assigning values to the variable you declared in the outer block. go vet with the -shadow=true option can help find such bugs.

How to understand this behavior of goroutine?

package main
import (
"fmt"
"time"
)
type field struct {
name string
}
func (p *field) print() {
fmt.Println(p.name)
}
func main() {
data := []field{ {"one"},{"two"},{"three"} }
for _,v := range data {
go v.print()
}
<-time.After(1 * time.Second)
}
why does this code print 3 "three" instead of "one" "two" "three" in any order?
There is a data race.
The code implicitly takes address of variable v when evaluating arguments to the goroutine function. Note that the call v.print() is shorthand for the call (&v).print().
The loop changes the value of variable v.
When goroutines execute, it so happens that v has the last value of the loop. That's not guaranteed. It could execute as you expected.
It's helpful and easy to run programs with the race detector. This data race is detected and reported by the detector.
One fix is to create another variable scoped to the inside of the loop:
for _, v := range data {
v := v // short variable declaration of new variable `v`.
go v.print()
}
With this change, the address of the inner variable v is taken when evaluating the arguments to the goroutine. There is a unique inner variable v for each iteration of the loop.
Yet another way to fix the problem is use a slice of pointers:
data := []*field{ {"one"},{"two"},{"three"} } // note '*'
for _, v := range data {
go v.print()
}
With this change, the individual pointers in the slice are passed to the goroutine, not the address of the range variable v.
Another fix is to use the address of the slice element:
data := []field{ {"one"},{"two"},{"three"} } // note '*'
for i:= range data {
v := &data[i]
go v.print()
}
Because pointer values are typically used with types having a pointer receiver, this subtle issue does not come up often in practice. Because field has a pointer receiver, it would be typical to use []*field instead of []field for the type of data in the question.
If the goroutine function is in an anonymous function, then a common approach for avoiding the issue is to pass the range variables as an argument to the anonymous function:
for _, v := range data {
go func(v field) {
v.print() // take address of argument v, not range variable v.
}(v)
}
Because the code in the question does not already use an anonymous function for the goroutine, the first approach used in this answer is simpler.
As stated above there’s a race condition it’s result depends on delays on different processes and not well defined and predictable.
For example if you add time.Sleep(1*time.Seconds) you likely to get a correct result. Because usually goroutine prints faster than 1second and will have correct variable v but it’s a very bad way.
Golang has a special race detector tool which helps to find such situations. I recommend read about it while reading testing. Definitely it’s worth it.
There’s another way - explicitly pass variable value at goroutine start:
for _, v := range data {
go func(iv field) {
iv.print()
}(v)
}
Here v will be copied to iv (“internal v”) on every iteration and each goroutine will use correct value.

Use of variable in 'for' loop is not recognized in Go

I'm developing in Go and I run the following for loop:
// Define Initial Value
i := 0
for {
// Get random data based on iteration
data, i := GiveRandomData(i)
// Save to database
response, err := SaveToDatabase(data)
if err != nil { log.Fatal(err) }
fmt.Println(response)
}
However, when compiling this program, I get the following error:
.\main.go:26: i declared and not used
The Go compiler doesn't seem to recognise that the i variable is given back to the function in the next loop. Inside this function, the I variable changes value.
What should I do to get rid of this compilation error or to let Go understand that this variable is not unused, but used in the next iteration of this endless for loop?
The Go compiler doesn't seem to recognise that the i variable is given back to the function in the next loop. Inside this function, the I variable changes value.
No, i does not change value; := declares a new i. (Go allows you to do this because data is also new.) To assign to it instead, you’ll need to declare data separately:
var data RandomDataType
data, i = GiveRandomData(i)
Or give the new i a temporary name:
data, next := GiveRandomData(i)
i = next

Resources