Therefore, just reading that sequence changes (consumes) it.
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.