let values_for_iter = [0i32, 1, 2].iter();
let mut iter_result = values_for_iter.take_while(|x| **x < 10 );
println!("values result is {:?}", iter_result.next());
Code with Range or RangeFrom:
let values_for_range = 0..;
let mut range_values = values_for_range.take_while(|x| *x < 10 );
println!("range values is {:?}", range_values.next());
Why double derefence not needed in Code with Range?
That would be because Range<i32> is an Iterator<i32>, whereas [i32]::iter() returns an Iterator<&i32>.
The reason for this discrepancy, in turn, is that iterating over a range consumes it (= destroys it), whereas iterating over an array does not consume it. If you do not consume the array, you can only return references to the elements, not move them away. Hence the Iterator<&i32>.
In an ideal world, you could consume the array and get an Iterator<i32> just by calling into_iter() on it instead of iter(), as done on Vec<i32>. Sadly, though, this implementation of IntoIterator was not included in the Rust standard library back when the language was stabilized, and as far as I know it cannot be added in a backward-compatible way so we're unlikely to ever see it...
For small types like i32, however, you can work around this by making inconsequential copies which will likely be optimized out by the compiler, via [1, 2, 3].iter().cloned().
I did not see it, I knew about it beforehand because I fell in the same trap that you did many times
Anyway, to answer your question, you can check that you're dealing with the kind of iterator that you expect to be dealing with by abusing generics / impl Trait a bit:
fn assert_iterator_i32(_: impl Iterator<Item=i32>) {}
// Will succesfully compile
assert_iterator_i32(0..10);
// Will cause a compiler error that contains the actual iterator type
assert_iterator_i32([0i32, 1, 2].iter());
In future Rust, you'll be able to use this little trick even more quickly via impl Trait bindings:
// Not in stable Rust, but scheduled for future Rust versions
let check: impl Iterator<Item=i32> = [0i32, 1, 2].iter();
Thanks @chrisd for the copied suggestion. With that double dereference is not required.
let values_for_iter = [0i32, 1, 2].iter().copied();
let mut iter_result = values_for_iter.take_while(|x| *x < 10 );
println!("values result is {:?}", iter_result.next());
Keep shamelessly using dirty impl Iterator tricks like the one I pointed out above. The compiler's error message, graciously sent to you by rustc's powerful trait engine, will tell you the actual item type of the iterator even if it's not a simple T vs &T matter.
I am not aware of an easy way to ask rustc for a full list of traits implemented by a type. Even if there were one, you probably wouldn't want to use it, because for some types that list can be very long, and grow anytime you add extra crates to your Cargo.toml...
Yeah, what I was looking for full list of all traits and I could grep those based on pattern.
I guess that is not there.
This was to save time instead of digging in for every such issue.
May be someday rust team could have such a function but I am guessing there are reasons why it is not there yet.
Is there a function that tells something is a reference vs moved ownership directly?
May be that would solve some of the issues.
Just make your best guess. If you guessed wrong, the compiler will tell you it got a &i32 but expected an i32 or vice versa, so it'll be rather easy to spot what is wrong. At some point you will get familiar with the iterators, and your guesses will typically be right.
Regarding reading the documentation, almost every other example will have a doc, where finding the type will be easier.
@alice: compiler and editor are great at helping to resolve the issue from point of view of reference being used or not. That part is easy - to make it work.
However, my intent was to know 'easily' exactly which Trait is forcing this to happen so my understanding of the codebase is more firm and not random
The point is not to make guesses and quickly trace the Trait involved.