Therefore, just reading that sequence changes (consumes) it.
fn main() {
let squares : Vec<u32> = (0..10).map(|x| x * x).collect();
println!("{:?}", squares.iter().min()); // Some(0)
println!("{:?}", squares.iter().max()); // Some(81)
}
Edit: Or a smaller change to the original, you can add a single .clone(): fn main() {
let squares = (0..10).map(|x| x * x);
println!("{:?}", squares.clone().min());
println!("{:?}", squares.max());
}
This clones the std::iter::Map<std::ops::Range<i32>>. Unlike the vec version, this will re-evaluate the |x|x*x sequence when .max() is called.There's no technical reason Map<Range<i32>> couldn't be made to implement the Copy trait, which would make the extra .clone() here unnecessary - but Rust's standard library has chosen to force you to make an explicit choice instead.
It does so by distinguishing IEnumerable<T> [1], and IEnumerator<T> [2]. The first would be the result of the map call (Select in C#'s LINQ), representing a sequence, that could be generated, and the later is the one, that actually generates it one by one, and has to be consumed.
I wonder why Rust did not take C#'s approach.
[1] https://docs.microsoft.com/en-us/dotnet/api/system.collectio...
[2] https://docs.microsoft.com/en-us/dotnet/api/system.collectio...
In C# Enumerable.Range and .Select (e.g. .map) internally create an immutable instance, that implements IEnumerable<T>, that describes what will have to be returned.
Then .Min internally takes IEnumerable<T>, calls its .GetEnumerator, that actually returns something, that can be consumed once and has a mutable state, and finds the minimum by actually consuming that iterator (all internally).
If C# also implemented Min on IEnumerator<T>:
var enumerator = collection.GetEnumerator();
var min = enumerator.Min();
var max = enumerator.Max(); // Runtime bug: Min() already the entire enumerator! InvalidOperationException?
There's basically nothing useful you can do with 'enumerator' after the first call to this hypothetical Min. Rust's stdlib tries to catch this bug by having min take ownership of/consume 'enumerator', instead of merely borrowing it, so you can't use it again: let iter = collection.iter();
let min = iter.min();
let max = iter.max(); // Compile time error: iter used after move
In C#, basically everything that can be is an IEnumerable<T>, and the stdlib is built around that fact. Does System.IO.File.ReadLines hit disk every time you re-enumerate it? Or does it store to a container? Many of the stream based APIs have a .ReadLine() method but no .ReadLines() method, presumably because they'd have to allocate a container to store the result in case enumerable be re-evaluated multiple times, and that seems bad if you don't need it?Rust seems to lean a lot more heavily on single-shot iterators.
let lines = stdin.lock().lines();
for line in lines { ... }
for line in lines { ... } // Compile time error: lines moved.
let lines = BufReader::new(File::open("input.tsv")?).lines();
for line in lines { ... }
for line in lines { ... } // Compile time error: lines moved.
"Does lines() hit disk/stdin every time you re-enumerate it?" is a trick question: You can't re-enumerate it.You can resolve these errors by being explicit about storing the input in a container and iterating that multiple times, or by re-opening the input file, or by resetting the File position and constructing a new BufReader, depending on which behavior you wanted for re-enumeration.
- Vec<T> implements IntoIterator<Item=T>
- &Vec<T> implements IntoIterator<Item=&T>
- &mut Vec<T> implements IntoIterator<Item=&mut T>
and consequently, - vec.into_iter() implements Iterator<Item=T>
- (&vec).into_iter() (or vec.iter()) implements Iterator<Item=&T>
- (&mut vec).into_iter() (or vec.iter_mut()) implements Iterator<Item=&mut T>I struggled with this when writing my first rust program. I was very surprised by the behaviour.
max does not change a sequence, it consumes the iterator.
I would expect it would have to be declared as mutable
max takes self (and not &self), so the ownership of the iterator is moved to the max method. The new owner can decide to bind the value mutably. This is always the case when a value is moved and changes ownership, e.g.:
https://play.rust-lang.org/?gist=50e6fe86e530baeabda1a7cca3c...
I'll get there one day, but it's hard to go back to having no idea what my compiler is trying to do.
// Make a iterator
let test_iterator = vec![1, 2, 3].into_iter();
// count() iterates over the elements and consumes them
let count = test_iterator.count();
// because the values have been consumed, this is not possible
for element in test_iterator{
println!("{}", element);
}
See: https://play.rust-lang.org/?gist=750dffd641ac237690defbf86da...The count method will consume every element, and return the length. This is because iterators are meant to be consumed when iterated. This is maybe inconvenient, but necessary because of the generic nature of the iterator trait: for some things that implement the iterator trait it is just not possible to get the max(), min() or count without consuming the iterator. So this would work:
// Make a iterator
let test = vec![1, 2, 3];
// Create an Iterator and consume it with count()
let count = test.iter().count();
println!("Count: {}\n", count);
// Create another Iterator and loop over it (consume it in each row)
for element in test.iter(){
println!("{}", element);
}
// Create another Iterator and consume it with min()
let min = test.iter().min();
println!("\nMin: {}", min.unwrap());
Try here: https://play.rust-lang.org/?gist=498ffd9afa163909745d8537e97...That means, there was probably the choice: try keeping it generic and consistent for all things that implement iterator or making these methods something that works fine with basic types but not at all with anything more complex.
So: Iterators don't know any value until they are consumed. That means iterators will be consumed most of the time (there are exceptions.
Also see: https://doc.rust-lang.org/std/iter/index.html#implementing-i... And: https://doc.rust-lang.org/std/iter/trait.Iterator.html