3 ms·
How often do you access your loop variables asynchronously?
by rcme 3y ago
How often do you access your loop variables asynchronously?
- amluto 3y agoRarely, if ever. But aliasing and unexpected mutability don’t need asynchrony or threading to be a problem. For example, the entire problem leading to this proposal is about unexpected mutability. You have a variable i, which presently has a specific value. You take a reference to it, and you give that reference to some other code that saves it. Then you mutate i, and you are suddenly surprised that the external code now has a reference to the new value.
- rcme 3y agoYes, but the reference isn't explicitly created. Rather, the reference is implicitly captured by a Go routine, so this happens in Go much more often than other languages because Go routines are a core feature of the language.
- amluto 3y agoI’m not sure what you mean. In the OP, the examples involve closures that capture an induction variable. I don’t think Goroutines per se are involved. (Although, again, in C++ you had better not capture a reference and then have the closure in question outlive the referent, and you need to literally type & to get a closure to capture by reference. In Rust, references created by closure follow the same rules as any other reference.) And the earlier proposal: https://github.com/golang/go/discussions/56010 https://github.com/golang/go/discussions/56010 gives an explicit reference (with an &) as its motivating example. So I still believe that the whole issue being addressed is bizarre from the perspective of a C, C++ or Rust programmer. (I’m having trouble thinking of another language that promotes a local variable to be a heap-allocated slot when one explicitly takes a reference to it. Most GC’d languages have local variables that are, themselves, references to the heap such that the referent (the heap slot / box / whatever you want to call it) will outlive the variable if the reference is copied. Pure languages may not strongly distinguish between a value and a reference to a value because the referent can’t change, so a copy is as good as the original. I think that Go is rather odd here, but I could easily be missing something.) (P.S. C++ also has extends the lifetime of temporaries if a const reference is taken, but only to the end of the scope. This is too magical for my taste. At least the heap isn’t involved.)
- comex 3y agoIt’s true that the variant of the issue involving explicit references seems pretty unique to Go. But there are plenty of languages that have the variant of the issue where a closure implicitly captures a local variable by reference. JavaScript used to have it until `let` was created (and still does have it if you don’t use `let`). And I ran into it recently in some closure-heavy Python code I wrote recently. (Python makes it relatively ugly to work around, too.)
- masklinn 3y ago> this happens in Go much more often than other languages I mean not really, a big reason for let and const in javascript was that this specific issue.
- wahern 3y agoIn most cases the reference is hidden in the context of a lexical closure. And arguably it's not about unexpected mutability, but the context of mutability. Nobody is expecting `i' to be immutable, but in the context of a for loop its demonstrably the case that most people intuitively expect `i' to be scoped to the inner block and not the looping statement itself; and that intuition is so strong that even people who thoroughly understand the implementation details still sometimes run afoul of the issue when mechanically banging out code. One can disagree with the wisdom of lexical closures, as opposed to other languages which require explicit declarations when binding variables or capturing values, but ultimately it's a tradeoff and lexical closures are a key architectural construct in Go. In this case, the implementation details resulted in poor cost/benefit, and tweaking the semantics of loop variables restores the cost/benefit in the context of Go's calculus.