5 ms·
So, for those who may know JavaScript, you may have seen code like this: var closures = []; for (var i=0;i<5;i++) { closures.push(function() {
by barosl 11y ago
So, for those who may know JavaScript, you may have seen code like this:
var closures = [];
for (var i=0;i<5;i++) {
closures.push(function() {
console.log(i);
});
}
The code above will result in incorrect results: 5, 5, 5, 5, 5. Because you're capturing `i` as a reference.
To avoid this, JS devs typically do this:
closures.push((function(i) {
return function() {
console.log(i);
};
})(i));
Or, if you can afford the ES6 support:
for (let i=0;i<5;i++) {
/* `let`-scoped `i`s are created individually at each iteration, so it is safe to capture them by references. */
}
Rust supports this pattern by a built-in syntax. That's what `move` means. If you prepend the `move` keyword before the closure, the variables will be captured by values, not references.
- kentosi 11y agoThanks for the explanation, but how would the move keyword know that you're referring to the i variable? Ie - what is there were multiple variables (such as a for loop with j in there)? Wouldn't it have been a better approach to add some sort of demarkation, such as i* or i^ (or whatever) to indicate this? Just curious.
- Chilinot 11y agoIt moves everything you are referencing in the closure from the outer scope to the closure.
- Manishearth 11y agoA closure captures all variables from its environment that it is using (no more than that). They can be captured by reference (which is the default), or by-move (which is done with the `move` keyword). In case it is being captured by move, _all_ captured variables will be moved. If you wish to capture a specific variable by reference in a move closure, create a reference (`let y = &x`) outside of the closure and use the reference y instead of x inside.
- pmarreck 11y agoI agree that this seems ugly
- sixbrx 11y agoIn short, the "move everything" nature of "move" keyword here is not an issue because you can just make references for items you don't want moved. References themselves are "moved" by just copying their addresses.
- masklinn 11y ago> Wouldn't it have been a better approach to add some sort of demarkation, such as i* or i^ (or whatever) to indicate this? That's the path C++ took[0], the Rust people thought it had too much syntactic and semantic overhead, and that having just "move" and "referring" closures would be much simpler. If you want to mix them up, it's easy enough to create references outside the closure (and capture them by value with a move closure) [0] http://en.cppreference.com/w/cpp/language/lambda#Lambda_capture http://en.cppreference.com/w/cpp/language/lambda#Lambda_capt...
- steveklabnik 11y agoThere was a really good thread on this on /r/urust: https://www.reddit.com/r/rust/comments/46w4g4/what_is_rusts_lambda_syntax_and_design_rationale/ https://www.reddit.com/r/rust/comments/46w4g4/what_is_rusts_... In particular, don't miss this post: https://www.reddit.com/r/rust/comments/46w4g4/what_is_rusts_lambda_syntax_and_design_rationale/d08hee0 https://www.reddit.com/r/rust/comments/46w4g4/what_is_rusts_...
- reycharles 11y agoThe ES6 "fix" is an abomination in my opinion. You're closing over a mutable variable, so you should see the mutations! IMO the real fix would be to write for (var i = 0; i < 5; i++) { let i_ = i; /* use i_ in the closure */ } In your ES6 solution if you e.g. increment "i" inside the loop after the closure the closure will see the mutation! The real cause of confusion is mutation and javascript's scoping rules.
- eddyb 11y agoIf you write `let data = data;` in the closure, then you can achieve the effect of `move` without the special syntax (unless `data` can be copied, in which case there is really no way to force it to be moved inside the closure without `move`). However, in Rust, you get an error if you accidentally capture by reference in the closure passed to `thread::spawn` so it's not as hard to get right as it is in JS.
- pornel 11y agoES6 fix is: for(let i = 0; i < 5; i++) { // no hacks needed if you don't use ES5 var }