-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
Introduce the IntoIterator
trait and reimplement for-loops to use it
#20790
Conversation
(rust_highfive has picked a reviewer for you, use r? to override) |
Can we do this without expanding in the front end? |
@huonw @nikomatsakis was interested in an implementation that used the frontend (hence this prototype) |
Also, expanding to include |
(I think @aturon and @nikomatsakis mainly want to measure the size of the fallout and the change in ergonomics of these new for-loop semantics before commiting to it. This implementation is minimal work to measure that. We can come up with a better implementation if we decide to actually do this.) |
@@ -273,7 +273,7 @@ impl<'a> Iterator for Recompositions<'a> { | |||
loop { | |||
match self.state { | |||
Composing => { | |||
for ch in self.iter { | |||
while let Some(ch) = self.iter.next() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could some loops like this be altered to:
for ch in self.iter.by_ref() { /* ... */ }
That may avoid consuming the entire iterator.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, if it helps in basically all cases, I wonder if we could spec it like this...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had forgotten about by_ref()
! I think we can use that instead of most (all?) the while let
s.
Actually, if it helps in basically all cases, I wonder if we could spec it like this...
You mean using by_ref()
in the for loop expansion?
Updated to use the |
Some thoughts:
|
@japaric and @nikomatsakis As a fan of desugaring as much as possible I think this seems like a good idea. If error messages become a problem you could use a similar scheme to what they do with Pyret where you provide a tag (either in some table or directly on the AST) for which rewrite rule you used, and then utilize that information when reporting errors by re-sugaring it then pretty printing. I would be happy to help explore such a scheme if the error reporting suffers. |
Updated PR with @nikomatsakis suggestions ( I've collected some error messages from 3 cfail tests related to for-loops:
struct MyStruct {
x: isize,
y: isize,
}
impl MyStruct {
fn next(&mut self) -> Option<isize> {
Option::Some(self.x)
}
}
fn main() {
let mut bogus = MyStruct {
x: 1,
y: 2,
};
for x in bogus {
//~^ old-ERROR has type `MyStruct` which does not implement the `Iterator` trait
//~^^ new-ERROR the trait `iter::Iterator` is not implemented for the type `MyStruct`
}
} The error message changed its wording but the meaning is the same.
fn main() {
for
&1i8
//~^ old-ERROR refutable pattern in `for` loop binding
//~^^ new-ERROR non-exhaustive patterns: `Some(&_)` not covered
in [1i8].iter() {}
} The message is more obscure now.
fn main() {
let x = () + (); //~ ERROR binary operation
// this shouldn't have a flow-on error:
// japaric: and it doesn't with the either version of for loops
for _ in x {}
} Behavior remains the same. |
@@ -65,6 +65,8 @@ | |||
#![allow(unknown_features)] #![feature(int_uint)] | |||
#![feature(on_unimplemented)] | |||
#![deny(missing_docs)] | |||
// SNAP 9e4e524 remove `allow(unused_mut)` after a snapshot | |||
#![allow(unused_mut)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now that we have cfg_attr
, could this be #![cfg_attr(stage0, allow(unused_mut))]
? Anything with the string "stage0" in it is a pretty easy flag for something to clean out when dealing with snapshots.
@japaric and I were talking on IRC. My feeling is that the error in this example: fn main() {
for
&1i8
//~^ old-ERROR refutable pattern in `for` loop binding
//~^^ new-ERROR non-exhaustive patterns: `Some(&_)` not covered
in [1i8].iter() {}
} is actually better. I doubt most "lay people" know what a "refutable pattern" is. (I personally have to work out from first principles which is "irrefutable" and which is "refutable".) But having a concrete counter example seems immediately understandable. It might be better still to combine the two messages:
|
@nikomatsakis I think introducing the |
IntoIterator
trait and reimplement for-loops to use itIntoIterator
trait and reimplement for-loops to use it
This PR is ready! (passed re: error message on refutable patterns, I decide to reuse the old message/diagnostic code, we can customize it later if desired. |
rebased and added tests for issues that new for loops fix. Also fixed a recursive call that the new |
Just a heads up, until #21637 gets fixed, we won't be able to use the fn concat<I: IntoIterator>(it: I) -> String where <I::Iter as Iterator>::Item: Str {
unimplemented!();
} |
@@ -0,0 +1,7 @@ | |||
std |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oops!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ugh..
/me snipes file
As per [RFC rust-lang#235][rfc], you can now do: [rfc]: https://github.com/rust-lang/rfcs/blob/master/text/0235-collections-conventions.md#intoiterator-and-iterable ``` rust let mut v = vec![1]; // iterate over immutable references for x in &v { assert_eq!(x, &1); } // iterate over mutable references for x in &mut v { assert_eq!(x, &mut 1); } // iterate over values, this consumes `v` for x in v { assert_eq!(x, 1); } ``` [breaking-change]s For loops now "consume" (move) the iterator, this breaks iterating over mutable references to iterators, and also breaks multiple iterations over the same iterator: ``` rust fn foo(mut it: &mut Iter) { // `Iter` implements `Iterator` for x in it { .. } //~ error: `&mut Iter` doesn't implement Iterator } fn bar() { for x in it { .. } //~ note: `it` moved here for x in it { .. } //~ error: `it` has been moved } ``` Both cases can be fixed using the `by_ref()` adapter to create an iterator from the mutable reference: ``` rust fn foo(mut it: &mut Iter) { for x in it.by_ref() { .. } } fn bar() { for x in it.by_ref() { .. } for x in it { .. } } ``` This PR also makes iterator non-implicitly copyable, as this was source of subtle bugs in the libraries. You can still use `clone()` to explictly copy the iterator. Finally, since the for loops are implemented in the frontend and use global paths to `IntoIterator`, `Iterator` and `Option` variants, users of the `core` crate will have to use add an `std` module to the root of their crate to be able to use for loops: ``` rust #![no_std] extern crate core; fn main() { for x in 0..10 {} } #[doc(hidden)] mod std { // these imports are needed to use for-loops pub use core::iter; pub use core::option; } ``` --- r? @nikomatsakis @aturon cc rust-lang#18424 closes rust-lang#18045
⌛ Testing commit b9a9030 with merge e47284b... |
@bors: retry |
⌛ Testing commit b9a9030 with merge e513946... |
💔 Test failed - auto-linux-64-opt |
@bors: retry |
⌛ Testing commit b9a9030 with merge 581a5c8... |
💔 Test failed - auto-linux-64-x-android-t |
@bors: retry |
⌛ Testing commit b9a9030 with merge 692c620... |
💔 Test failed - auto-linux-32-opt |
@bors: retry |
⌛ Testing commit b9a9030 with merge 72eab3e... |
Removes `Copy` from `ops::Range` (`a..b`) and `ops::RangeFrom` (`a..`) [breaking-change] --- I forgot about these two in #20790, this PR also adds `Clone` to the `Peekable` adapter which used to be `Copy`able. r? @nikomatsakis or anyone
The RFC didn't mention this "mod std" business, and I'm a bit uncomfortable with it. Why not directly access the core crate? |
If you mean expanding to
You already have to use the |
See rust-lang/rust#20790 for details.
The relevant section of the Rust Reference ought to be updated to reflect this, cc @steveklabnik |
As per RFC #235, you can now do:
[breaking-change]s
For loops now "consume" (move) the iterator, this breaks iterating over mutable references to iterators, and also breaks multiple iterations over the same iterator:
Both cases can be fixed using the
by_ref()
adapter to create an iterator from the mutable reference:This PR also makes iterator non-implicitly copyable, as this was source of subtle bugs in the libraries. You can still use
clone()
to explictly copy the iterator.Finally, since the for loops are implemented in the frontend and use global paths to
IntoIterator
,Iterator
andOption
variants, users of thecore
crate will have to use add anstd
module to the root of their crate to be able to use for loops:r? @nikomatsakis @aturon
cc #18424
closes #18045