Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign upHave `for pattern in iter` not borrow the iterator #8372
Comments
This comment has been minimized.
This comment has been minimized.
|
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. |
This comment has been minimized.
This comment has been minimized.
|
@thestinger |
This comment has been minimized.
This comment has been minimized.
|
@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. |
This comment has been minimized.
This comment has been minimized.
|
Also, this is a "nice to have", but not blocking because I can just use
|
This comment has been minimized.
This comment has been minimized.
|
Your macro should probably take an ident, not an expression since it doesn't work well with expressions! |
This comment has been minimized.
This comment has been minimized.
|
Good point, @blake2-ppc.
|
thestinger
referenced this issue
Aug 20, 2013
Closed
Forgetting .iter() in for loop causes confusing type error message #8649
This comment has been minimized.
This comment has been minimized.
|
triage, no progress. |
This comment has been minimized.
This comment has been minimized.
|
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. |
This comment has been minimized.
This comment has been minimized.
|
cc me |
This comment has been minimized.
This comment has been minimized.
It seems to me that there is precedent for language constructs to act specially when the expression in question is an lvalue, c.f. |
This comment has been minimized.
This comment has been minimized.
|
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. |
This comment has been minimized.
This comment has been minimized.
|
|
This comment has been minimized.
This comment has been minimized.
|
@blake2-ppc could you write the expansion you are thinking of? (I'm specifically wondering how that would allow for something like |
This comment has been minimized.
This comment has been minimized.
|
Solved by #15809 |
This comment has been minimized.
This comment has been minimized.
|
@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());
}
}
|
SimonSapin
closed this
Jul 30, 2014
SimonSapin
added a commit
to SimonSapin/rust
that referenced
this issue
Jul 30, 2014
alexcrichton
added a commit
to alexcrichton/rust
that referenced
this issue
Jul 31, 2014
alexcrichton
added a commit
to alexcrichton/rust
that referenced
this issue
Jul 31, 2014
alexcrichton
added a commit
to alexcrichton/rust
that referenced
this issue
Jul 31, 2014
alexcrichton
added a commit
to alexcrichton/rust
that referenced
this issue
Jul 31, 2014
alexcrichton
added a commit
to alexcrichton/rust
that referenced
this issue
Jul 31, 2014
alexcrichton
added a commit
to alexcrichton/rust
that referenced
this issue
Jul 31, 2014
pcwalton
added a commit
to pcwalton/rust
that referenced
this issue
Sep 8, 2014
pcwalton
referenced this issue
Sep 8, 2014
Merged
librustc: Make sure lifetimes in `for` loop heads outlive the `for` loop #17101
This comment has been minimized.
This comment has been minimized.
|
Re-opening, see #17101. |
SimonSapin
reopened this
Sep 8, 2014
This comment has been minimized.
This comment has been minimized.
|
Why has this been removed? The other PR only talks about rvalues while this is about lvalues. You can even do it with a macro: #![feature(macro_rules)]
macro_rules! each {
($e:ident in $y:ident, $b:block) => {{
loop {
let $e = match $y.next() {
Some(e) => e,
None => break,
};
$b;
}
}}
}
fn main() {
let mut x = range(0, 10i);
each!(y in x, {
println!("{}", y);
x.next();
});
}This change breaks a serious amount of code. |
This comment has been minimized.
This comment has been minimized.
|
The implementation we had was incorrect and memory unsafe. |
This comment has been minimized.
This comment has been minimized.
|
That's what it says in #17101. |
This comment has been minimized.
This comment has been minimized.
|
@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) |
This comment has been minimized.
This comment has been minimized.
|
@pnkfelix: I chose I don't know how the |
This comment has been minimized.
This comment has been minimized.
|
I’d expect the |
This comment has been minimized.
This comment has been minimized.
|
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 |
This comment has been minimized.
This comment has been minimized.
|
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? |
This comment has been minimized.
This comment has been minimized.
|
(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). |
This comment has been minimized.
This comment has been minimized.
|
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 |
This comment has been minimized.
This comment has been minimized.
|
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 |
This comment has been minimized.
This comment has been minimized.
|
@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 |
This comment has been minimized.
This comment has been minimized.
|
You can now just use while let Some(item) = iterator.next() {
// ...
}It's not perfect but but better than using a macro (IMHO). |
This comment has been minimized.
This comment has been minimized.
|
Oooh, this is much nicer indeed. In particular since there is only one nesting level of |
This comment has been minimized.
This comment has been minimized.
smosher
commented
Dec 19, 2014
|
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? |
This comment has been minimized.
This comment has been minimized.
|
Now that On Thu, Dec 18, 2014 at 07:46:54PM -0800, Stephen Mosher wrote:
|
This comment has been minimized.
This comment has been minimized.
|
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
|
This comment has been minimized.
This comment has been minimized.
|
I still kinda wish this was fixed, but I understand @nikomatsakis’s argument and I don’t mind as much with the |
P1start
referenced this issue
Jan 28, 2015
Merged
Introduce the `IntoIterator` trait and reimplement for-loops to use it #20790
This comment has been minimized.
This comment has been minimized.
|
#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. |
This comment has been minimized.
This comment has been minimized.
|
Fair enough. I’m happy with the |
SimonSapin commentedAug 7, 2013
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?