On Tue, Jul 19, 2022 at 08:28:02AM -0700, 'andreas graeper' via golang-nuts wrote:
> for i=0;i<10;i++ { go func () { /* const c=i OR var v=i */ > fmt.Println("f:beg i=",i) // here c or v instead > // action > fmt.Println("f:end i=",i) // here c or v instead > }} > when this routines get interrupted then beg-i and end-i differ > now i want at the beginning of the routine a const copy `const c int = i` > but its not allowed and `var v int = i` does not work, too. > > does an instance of a go-routine has no own stack-frame like a normal > function (c i.e) and therein local/private variables ? This? https://github.com/golang/go/wiki/CommonMistakes#using-goroutines-on-loop-iterator-variables In short: - Your goroutines run so-called anonymous functions - those defined "inline" and having no name, some folks call them "lambdas". Anonymous functions works like "closures": they "close over" any variable accessed in such a function but declared in any of the outer scopes accessible to the function. - A closure captures its external variables via reference - that is, if you create multiple closures in the same lexical scope, they all will reference the same variables. To say that in other words, they do not receive copies of such variables; they reference the original variables. - In your code snippet you start 10 goroutines passing each a new closure, each referencing the same loop variable, i. Hence, in your example you create the condition for a data race of 10 goroutines over the same variable i. The behaviour of this code is hence undefined. If you were to `go build -race` it (or `go run -race`, FWIW), the runtime would crash your running program. Conceptually two solutions are possible: - Make your anonymous functions not close over that shared variable. This can be done by defining one or more arguments in the function's definition and pass the values the function has to operate on via that parameters when calling it. Since passing of parameters is done via copying them, each anonymous function will have their own copies. In your simple case you could do what PeterGo proposed - something like: go func (int i) { ... }(i) If the set of variables to operate is huge so as being impractical to be passed via arguments, you usually either define a helper `struct` type containing all the required stuff and pass it as a single parameter or stop using anonymous functions altogether and define a custom type with a method, and to run a gorotine, you construct an instance of this type and then call the goroutine to run that method on that instance. (To recap: do not be attached to passing anonymous functions to goroutines: it indeed is a cool feature, often useful, but it is so widely demonstrated by various guides and blog posts that some novice Go programmers do not even understand that you can pass anything "callable" to the `go` statement, not only an anonymous function. Yes, I've witnessed people with this mind wart in interviews at my $dayjob.) - Create a copy of the loop iteration variable and capture _it_ in the anonymous function - like this: for i := 0, i < 10; i++ { i := i go func() { fmt.Println(i) }() } This "trick" works because in Go, all loop statements have two scopes: the outer scope is where the loop iteration variables live, and the inner one is the loop's body; hence in the presented example we create a variable named "i" in the inner scope and initialize it with the value currently held in the variable named "i" from the outer scope. The "inner" "i" is different on each iteration - or, to say that in other words, - is recreated anew on each iteration, so it's safe to capture it in a goroutine started on that iteration - provided, of course, that it's the sole goroutine capturing that variable and started on that iteration. What approach to pick depends on the use case, personal aesthetics and whatever the person(s) doing code review on your team prefer. -- You received this message because you are subscribed to the Google Groups "golang-nuts" group. To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/20220720100059.44upqj4vcqpnuf7d%40carbon.