-
Notifications
You must be signed in to change notification settings - Fork 13k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Have for pattern in iter
not borrow the iterator
#8372
Comments
This means implementing it as more than a simple parser feature. It's not possible to check the type of the passed in variable at the moment and decide whether a temporary is needed. |
@thestinger |
@thestinger what you say might still be true, but this is the type of AST node (eg. identifier) for the iterator, not Rust type (eg. |
Also, this is a "nice to have", but not blocking because I can just use
|
Your macro should probably take an ident, not an expression since it doesn't work well with expressions! |
Good point, @blake2-ppc.
|
triage, no progress. |
It would be good to decide what we're going to do about these things. (This and #8649) But its not clear whether it should block 1.0. |
cc me |
It seems to me that there is precedent for language constructs to act specially when the expression in question is an lvalue, c.f. |
Another painful example: fn main() {
let s = "foobar bazbang";
let mut chars = s.chars();
loop {
match chars.next() {
Some(c) => {
if c == ' ' {
chars.next();
} else {
print!("{}", c);
}
},
None => { break; }
}
}
} is good, but: fn main() {
let s = "foobar bazbang";
let mut chars = s.chars();
for c in chars {
if c == ' ' {
chars.next();
} else {
print!("{}", c);
}
}
} is not. |
|
@blake2-ppc could you write the expansion you are thinking of? (I'm specifically wondering how that would allow for something like |
Solved by #15809 |
@pczarn, indeed it is, thanks for the heads up! Updated for language/lib changes, the initial test case now works as expected: fn main() {
let mut iter = range(1i, 10);
for i in iter {
println!("{}", i);
println!("{}", iter.next());
}
}
|
Closes rust-lang#16097 (fix variable name in tutorial) Closes rust-lang#16100 (More defailbloating) Closes rust-lang#16104 (Fix deprecation commment on `core::cmp::lexical_ordering`) Closes rust-lang#16105 (fix formatting in pointer guide table) Closes rust-lang#16107 (remove serialize::ebml, add librbml) Closes rust-lang#16108 (Fix heading levels in pointer guide) Closes rust-lang#16109 (rustrt: Don't conditionally init the at_exit QUEUE) Closes rust-lang#16111 (hexfloat: Deprecate to move out of the repo) Closes rust-lang#16113 (Add examples for GenericPath methods.) Closes rust-lang#16115 (Byte literals!) Closes rust-lang#16116 (Add a non-regression test for issue rust-lang#8372) Closes rust-lang#16120 (Deprecate semver) Closes rust-lang#16124 (Deprecate uuid) Closes rust-lang#16126 (Deprecate fourcc) Closes rust-lang#16127 (Remove incorrect example) Closes rust-lang#16129 (Add note about production deployments.) Closes rust-lang#16131 (librustc: Don't ICE when trying to subst regions in destructor call.) Closes rust-lang#16133 (librustc: Don't ICE with struct exprs where the name is not a valid struct.) Closes rust-lang#16136 (Implement slice::Vector for Option<T> and CVec<T>) Closes rust-lang#16137 (alloc, arena, test, url, uuid: Elide lifetimes.)
itself. This breaks code like: for &x in my_vector.iter() { my_vector[2] = "wibble"; ... } Change this code to not invalidate iterators. For example: for i in range(0, my_vector.len()) { my_vector[2] = "wibble"; ... } The `for-loop-does-not-borrow-iterators` test for rust-lang#8372 was incorrect and has been removed. Closes rust-lang#16820. [breaking-change]
Re-opening, see #17101. |
The implementation we had was incorrect and memory unsafe. |
That's what it says in #17101. |
@mahkoh I do not think your macro properly captures what people expect from for-loops. In particular, I ported the example from #16820 over to the macro above, and (even after generalizing it to take a But then if you fix that problem in the "obvious" manner, you are back where you started: http://is.gd/3EQAly This is the problem that one must solve in some manner, the one that I referred to in my comment here: #17101 (comment) |
@pnkfelix: I chose I don't know how the |
I’d expect the |
Yeah definitely, the idea is sound, the implementation just wasn't actually implementing that idea. (I have to admit, I'm confused why @mahkoh decided to ask "why was this removed?" despite acknowledging that they knew the reason was the implementation was wrong. In any case, the situation we have now is just a temporary revert back to the old behaviour that we had when |
From the comments in this thread it looks like this worked from July 30th until at least September 5th. I don't see where you get the 9 days from. And I don't understand why the rvalue bug had to be fixed so urgently that this case had to be removed with it. Even simply checking if the "iterator" is a single identifier would have kept the cases discussed in this issue working. I don't know if the old version was also unsafe in that case, but if not, then simply adding an if-block around the code added by @pcwalton should do the trick, no? |
(Oh, whoops, read those dates wrong; even so, only 40 days, and we had lived with this for nearly a year before that.) Being memory safe and correct is very urgent: as it stood, our loop {
let pat = match iter.next() { Some(x) => x, None => break }; This doesn't seem particularly burdensome compared to 'have an incorrect compiler'. The fixing-the-horrible-bug patch is being conservative, but it is has the rather large advantage of being correct (and is clearly so). |
Am I correct in my assumption that this could be fixed very easily with a syntax extension? I don't see how something so simple that |
It's simple to write a syntax extension for lvalues where it doesn't borrow the iterator within the body of the loop. It does have to borrow the iterator temporarily at the top of the loop body to call |
@TyOverby Sure, this can be done with It’s unfortunate that we have to switch to an entirely different syntactic construct for something this semantically is a |
You can now just use while let Some(item) = iterator.next() {
// ...
} It's not perfect but but better than using a macro (IMHO). |
Oooh, this is much nicer indeed. In particular since there is only one nesting level of |
As far as blocking 1.0 goes, it won't bother me personally if this doesn't get fixed until after. But it might be confusing and offputting to newcomers who already have a lot to learn about ownership. (I know I've invented a few new things to say after "why the ...?" beacuse of this issue.) Might it be possible to detect the situation and emit a hint about the workaround? |
Now that On Thu, Dec 18, 2014 at 07:46:54PM -0800, Stephen Mosher wrote:
|
I used to be annoyed about this a ton, but now that we have On Sat, Dec 20, 2014 at 9:08 PM, Niko Matsakis notifications@github.com
|
I still kinda wish this was fixed, but I understand @nikomatsakis’s argument and I don’t mind as much with the |
#20790 implemented the new for-loops that take the iterator by value, so this issue can't be fixed (without changing the semantics in a backwards incompatible way). This should be closed. |
Fair enough. I’m happy with the |
Due to rust-lang/rust#8372, we have to use while-let in these cases.
Due to rust-lang/rust#8372, we have to use while-let in these cases.
Due to rust-lang/rust#8372, we have to use while-let in these cases.
make unwrap_used also trigger on .get().unwrap() fixes rust-lang#8124 changelog: make the [unwrap_used] lint trigger for code of the form such as `.get(i).unwrap()` and `.get_mut(i).unwrap()`
Test case:
The above currently fails, but I think it should be valid and equivalent to:
In other words, the for loop should only borrow the iterator long enough to call
next()
, leaving the loop body free to use it.I understand that this might not be possible when a more complex expression is used as the iterator. The for loop needs to evaluate the expression once and keep the result in an anonymous variable, thus borrowing. Maybe there could be a special case when the iterator expression is "simple" enough: a variable, struct field access, etc?
The text was updated successfully, but these errors were encountered: