This code won't compile:
fn main() {
let m1 = vec![1, 2, 3];
let m2 = vec![&m1, &m1, &m1];
let m3 = vec![vec![1, 2, 3], vec![4, 5, 6], vec![7, 8, 9]];
for i in &m2 {
for j in i {
println!("{}", j);
}
}
for i in &m3 {
for j in i {
println!("{}", j);
}
}
}
error[E0277]: the trait bound `&&std::vec::Vec<{integer}>: std::iter::Iterator` is not satisfied
--> src/main.rs:8:18
|
8 | for j in i {
| ^ `&&std::vec::Vec<{integer}>` is not an iterator; maybe try calling `.iter()` or a similar method
|
= help: the trait `std::iter::Iterator` is not implemented for `&&std::vec::Vec<{integer}>`
= note: required by `std::iter::IntoIterator::into_iter`
How is m2 different than m3 such that m3 causes no issues, but m2 prevents compilation?
Is there an easier way to create a vector of vector of... to any desired depth? The way I have it working (m3) seems so clunky.
How is m2 different than m3 ...
Check the types step by step. m1 is of type Vec<isize> (it could be any other integer type as well, but I assume it's isize for now). Why? Because the elements in the vec![] macro are of type isize. Now you are creating m2:
let m2 = vec![&m1, &m1, &m1];
What is the type of the elements in this macro? Well we already said m1 has the type Vec<isize>, so &m1 has the type &Vec<isize>. So the resulting type of m2 is Vec<&Vec<isize>> (a vector full of references to other vectors).
However, m3 is of type Vec<Vec<isize>>, since the elements in the (outer) vec![] macro are of type Vec<isize> (no reference!).
Hint: to easily check the type of any variable (such as foo), type:
let _: () = foo;
This will result in a compiler error that tells you the type of foo.
... such that m3 causes no issues, but m2 prevents compilation?
Now that we know the types of m2 and m3, lets look at the loops. for loops work by accepting something that implements IntoIterator. You are passing &m2, which is of type &Vec<&Vec<isize>> (note the two references). We can see, that IntoIterator is indeed implemented for a reference to a vector:
impl<T> IntoIterator for &Vec<T> {
type Item = &T
// ...
}
This means that you get an iterator that spits out references to the inner type T (type Item = &T). Our inner type of m2 is &Vec<isize>, so we will get items of type &&Vec<isize> (two references!). Your variable i has this exact type.
Then you want to iterate again with this inner loop:
for j in i { ... }
But i has this double-reference type and there isn't an implementation of IntoIterator for that type. To iterate it, you have to either dereference it like:
for j in *i { ... }
Or even better: make i be of the type &Vec<isize> (one reference!) by stripping it away with pattern matching in the outer loop:
for &i in &m2 { ... }
Your m3 loop does the same, but since m3 is of another type (with one reference less), it works (I hope you can see why).
Is there an easier way to create a vector of vector of... to any desired depth
Even if m2 worked, it wouldn't hold the same values as m3. To make m2 of the type Vec<Vec<isize>> (like m3), you should clone m1 instead of taking a reference to it.
let m2 = vec![m1.clone(), m1.clone(), m1.clone()];
We can do even better by using the vec![_; _] form of the macro:
let m2 = vec![m1; 3]; // three times the value of `m1`
As a last note, you should consider not using nested Vecs. The nesting creates overhead because the values are spread over the whole memory instead of being in one place.
Related
Suppose I have the following:
fn into_three_tuple(mut v: Vec<String>) -> (String, String, String) {
if v.len() == 3 {
// ???
} else {
panic!()
}
}
What's should I replace ??? with to achieve the best performance?
Possible solutions
Sure, I could do
...
if v.len() == 3 {
let mut iter = v.into_iter();
(v.next().unwrap(), v.next().unwrap(), v.next().unwrap())
} else {
...
or similarly
if v.len() == 3 {
let mut iter = v.into_iter();
let e2 = v.pop.unwrap();
let e1 = v.pop.unwrap();
let e0 = v.pop.unwrap();
(e0, e1, e2)
} else {
...
Problems with those solutions
Both of these implementations use unwrap, which, if I understand correctly, performs a runtime check. But since we have the v.len() == 3 condition, we know the vector is guaranteed to have 3 elements, so the the runtime check is unnecessary.
Also, the into_iter solution may introduce the additional overhead of creating the iterator, and the pop solution may introduce the additional overhead of decreasing the v's internal len field, which seems silly since v will be dropped immediately after extracting the elements (so we don't care whether its len field is accurate).
Question
Is there some (possibly unsafe) way that's more efficient (e.g., some way to directly take ownership of elements at arbitrary indices)?
Or perhaps the compiler is already smart enough to skip these extraneous operations?
Or do I just have to live with suboptimal performance?
Note
In case you're wondering why I'm obsessing over such a tiny micro-optimization, I'm writing a fairly speed-critical application, and this function will be called a significant amount of times.
There is an operation provided in the standard library to extract a fixed number of items from a vector: converting it to an array.
use std::convert::TryInto; // needed only if not using 2021 edition / Rust 1.55 or earlier
pub fn into_three_tuple(v: Vec<String>) -> (String, String, String) {
let three_array: [String; 3] = v.try_into().unwrap();
let [a, b, c] = three_array;
(a, b, c)
}
I'm not really familiar with reading x86 assembly, but this does compile down to simpler code with fewer branches. I would generally expect that this is in most cases the fastest way to unpack a three-element vector; if it is reliably slower, then that would be a performance bug in the standard library which should be reported and fixed.
You should also consider using an array [String; 3] instead of a tuple in the rest of your program. The type is shorter to write, and they allow you to use array and slice operations to act on all three strings. Additionally, tuples do not have a guaranteed memory layout, and arrays do (even though practically they are likely to be identical in this case).
Changing the return type to be an array makes the function trivial — possibly useful for the type declaration, but not containing any interesting code:
pub fn into_three_array(v: Vec<String>) -> [String; 3] {
v.try_into().unwrap()
}
Disclaimer: As mentioned in the comments, you should benchmark to check that any of this actually makes a difference in your program. The fact that you're using many 3-element vectors (which are heap-allocated and therefore comparatively inefficient) shows that you may be over-optimizing, or optimizing at the wrong place. Having said that...
the into_iter solution may introduce the additional overhead of creating the iterator
Note that the "iterator" is a tiny on-stack value entirely transparent to the compiler, which can proceed to inline/eliminate it entirely.
Or perhaps the compiler is already smart enough to skip these extraneous operations?
In many cases, a check for v.len() == <concrete number> is indeed sufficient for the compiler to omit bounds checking because it has proof of the vector size. However, that doesn't appear to work with the approaches you've tried. After modifying the code to std::process::exit() if v.len() != 3 so the only panic is from the runtime checks, the runtime checks (as evidence by calls to panic) are still not removed either with the .pop() or with the into_iter() approach.
Is there some (possibly unsafe) way that's more efficient (e.g., some way to directly take ownership of elements at arbitrary indices)?
Yes. One approach is to use unreachable_unchecked() to avoid the panic where we can prove the calls to next() will succeed:
use std::hint::unreachable_unchecked;
pub fn into_three_tuple(v: Vec<String>) -> (String, String, String) {
if v.len() == 3 {
let mut v = v.into_iter();
unsafe {
let e0 = v.next().unwrap_or_else(|| unreachable_unchecked());
let e1 = v.next().unwrap_or_else(|| unreachable_unchecked());
let e2 = v.next().unwrap_or_else(|| unreachable_unchecked());
(e0, e1, e2)
}
} else {
panic!()
}
}
Modifying the code in the same way as the above shows no panic-related code.
Still, that relies on the compiler being smart enough. If you want to ensure the bound checks are not done, Rust unsafe allows you to do that as well. You can use as_ptr() to obtain a raw pointer to the elements stored in the vector, and read them from there directly. You need to call set_len() to prevent the vector from dropping the elements you've moved, but to still allow it to deallocate the storage.
pub fn into_three_tuple(mut v: Vec<String>) -> (String, String, String) {
if v.len() == 3 {
unsafe {
v.set_len(0);
let ptr = v.as_ptr();
let e0 = ptr.read();
let e1 = ptr.add(1).read();
let e2 = ptr.add(2).read();
(e0, e1, e2)
}
} else {
panic!("expected Vec of length 3")
}
}
The generated code again shows no bound check related panics, which is expected because there are no calls to functions that performs a checked access to data.
I am doing the Rust by Example tutorial which has this code snippet:
// Vec example
let vec1 = vec![1, 2, 3];
let vec2 = vec![4, 5, 6];
// `iter()` for vecs yields `&i32`. Destructure to `i32`.
println!("2 in vec1: {}", vec1.iter() .any(|&x| x == 2));
// `into_iter()` for vecs yields `i32`. No destructuring required.
println!("2 in vec2: {}", vec2.into_iter().any(| x| x == 2));
// Array example
let array1 = [1, 2, 3];
let array2 = [4, 5, 6];
// `iter()` for arrays yields `&i32`.
println!("2 in array1: {}", array1.iter() .any(|&x| x == 2));
// `into_iter()` for arrays unusually yields `&i32`.
println!("2 in array2: {}", array2.into_iter().any(|&x| x == 2));
I am thoroughly confused — for a Vec the iterator returned from iter yields references and the iterator returned from into_iter yields values, but for an array these iterators are identical?
What is the use case/API for these two methods?
TL;DR:
The iterator returned by into_iter may yield any of T, &T or &mut T, depending on the context.
The iterator returned by iter will yield &T, by convention.
The iterator returned by iter_mut will yield &mut T, by convention.
The first question is: "What is into_iter?"
into_iter comes from the IntoIterator trait:
pub trait IntoIterator
where
<Self::IntoIter as Iterator>::Item == Self::Item,
{
type Item;
type IntoIter: Iterator;
fn into_iter(self) -> Self::IntoIter;
}
You implement this trait when you want to specify how a particular type is to be converted into an iterator. Most notably, if a type implements IntoIterator it can be used in a for loop.
For example, Vec implements IntoIterator... thrice!
impl<T> IntoIterator for Vec<T>
impl<'a, T> IntoIterator for &'a Vec<T>
impl<'a, T> IntoIterator for &'a mut Vec<T>
Each variant is slightly different.
This one consumes the Vec and its iterator yields values (T directly):
impl<T> IntoIterator for Vec<T> {
type Item = T;
type IntoIter = IntoIter<T>;
fn into_iter(mut self) -> IntoIter<T> { /* ... */ }
}
The other two take the vector by reference (don't be fooled by the signature of into_iter(self) because self is a reference in both cases) and their iterators will produce references to the elements inside Vec.
This one yields immutable references:
impl<'a, T> IntoIterator for &'a Vec<T> {
type Item = &'a T;
type IntoIter = slice::Iter<'a, T>;
fn into_iter(self) -> slice::Iter<'a, T> { /* ... */ }
}
While this one yields mutable references:
impl<'a, T> IntoIterator for &'a mut Vec<T> {
type Item = &'a mut T;
type IntoIter = slice::IterMut<'a, T>;
fn into_iter(self) -> slice::IterMut<'a, T> { /* ... */ }
}
So:
What is the difference between iter and into_iter?
into_iter is a generic method to obtain an iterator, whether this iterator yields values, immutable references or mutable references is context dependent and can sometimes be surprising.
iter and iter_mut are ad-hoc methods. Their return type is therefore independent of the context, and will conventionally be iterators yielding immutable references and mutable references, respectively.
The author of the Rust by Example post illustrates the surprise coming from the dependence on the context (i.e., the type) on which into_iter is called, and is also compounding the problem by using the fact that:
IntoIterator is not implemented for [T; N], only for &[T; N] and &mut [T; N] -- it will be for Rust 2021.
When a method is not implemented for a value, it is automatically searched for references to that value instead
which is very surprising for into_iter since all types (except [T; N]) implement it for all 3 variations (value and references).
Arrays implement IntoIterator (in such a surprising fashion) to make it possible to iterate over references to them in for loops.
As of Rust 1.51, it's possible for the array to implement an iterator that yields values (via array::IntoIter), but the existing implementation of IntoIterator that automatically references makes it hard to implement by-value iteration via IntoIterator.
I came here from Google seeking a simple answer which wasn't provided by the other answers. Here's that simple answer:
iter() iterates over the items by reference
iter_mut() iterates over the items, giving a mutable reference to each item
into_iter() iterates over the items, moving them into the new scope
So for x in my_vec { ... } is essentially equivalent to my_vec.into_iter().for_each(|x| ... ) - both move the elements of my_vec into the ... scope.
If you just need to look at the data, use iter, if you need to edit/mutate it, use iter_mut, and if you need to give it a new owner, use into_iter.
This was helpful: http://hermanradtke.com/2015/06/22/effectively-using-iterators-in-rust.html
I think there's something to clarify a bit more. Collection types, such as Vec<T> and VecDeque<T>, have into_iter method that yields T because they implement IntoIterator<Item=T>. There's nothing to stop us to create a type Foo<T> if which is iterated over, it will yield not T but another type U. That is, Foo<T> implements IntoIterator<Item=U>.
In fact, there are some examples in std: &Path implements IntoIterator<Item=&OsStr> and &UnixListener implements IntoIterator<Item=Result<UnixStream>>.
The difference between into_iter and iter
Back to the original question on the difference between into_iter and iter. Similar to what others have pointed out, the difference is that into_iter is a required method of IntoIterator which can yield any type specified in IntoIterator::Item. Typically, if a type implements IntoIterator<Item=I>, by convention it has also two ad-hoc methods: iter and iter_mut which yield &I and &mut I, respectively.
What it implies is that we can create a function that receives a type that has into_iter method (i.e. it is an iterable) by using a trait bound:
fn process_iterable<I: IntoIterator>(iterable: I) {
for item in iterable {
// ...
}
}
However, we can't* use a trait bound to require a type to have iter method or iter_mut method, because they're just conventions. We can say that into_iter is more widely useable than iter or iter_mut.
Alternatives to iter and iter_mut
Another interesting thing to observe is that iter is not the only way to get an iterator that yields &T. By convention (again), collection types SomeCollection<T> in std which have iter method also have their immutable reference types &SomeCollection<T> implement IntoIterator<Item=&T>. For example, &Vec<T> implements IntoIterator<Item=&T>, so it enables us to iterate over &Vec<T>:
let v = vec![1, 2];
// Below is equivalent to: `for item in v.iter() {`
for item in &v {
println!("{}", item);
}
If v.iter() is equivalent to &v in that both implement IntoIterator<Item=&T>, why then does Rust provide both? It's for ergonomics. In for loops, it's a bit more concise to use &v than v.iter(); but in other cases, v.iter() is a lot clearer than (&v).into_iter():
let v = vec![1, 2];
let a: Vec<i32> = v.iter().map(|x| x * x).collect();
// Although above and below are equivalent, above is a lot clearer than below.
let b: Vec<i32> = (&v).into_iter().map(|x| x * x).collect();
Similarly, in for loops, v.iter_mut() can be replaced with &mut v:
let mut v = vec![1, 2];
// Below is equivalent to: `for item in v.iter_mut() {`
for item in &mut v {
*item *= 2;
}
When to provide (implement) into_iter and iter methods for a type
If the type has only one “way” to be iterated over, we should implement both. However, if there are two ways or more it can be iterated over, we should instead provide an ad-hoc method for each way.
For example, String provides neither into_iter nor iter because there are two ways to iterate it: to iterate its representation in bytes or to iterate its representation in characters. Instead, it provides two methods: bytes for iterating the bytes and chars for iterating the characters, as alternatives to iter method.
* Well, technically we can do it by creating a trait. But then we need to impl that trait for each type we want to use. Meanwhile, many types in std already implement IntoIterator.
.into_iter() is not implemented for a array itself, but only &[]. Compare:
impl<'a, T> IntoIterator for &'a [T]
type Item = &'a T
with
impl<T> IntoIterator for Vec<T>
type Item = T
Since IntoIterator is defined only on &[T], the slice itself cannot be dropped the same way as Vec when you use the values. (values cannot be moved out)
Now, why that's the case is a different issues, and I'd like to learn myself. Speculating: array is the data itself, slice is only a view into it. In practice you cannot move the array as a value into another function, just pass a view of it, so you cannot consume it there either.
IntoIterator and Iterator are usually used like this.
We implement IntoIterator for structures that has an inner/nested value (or is behind a reference) that either implements Iterator or has an intermediate "Iter" structure.
For example, lets create a "new" data structure:
struct List<T>;
// Looks something like this:
// - List<T>(Option<Box<ListCell<T>>>)
// - ListCell<T> { value: T, next: List<T> }
We want this List<T> to be iterable, so this should be a good place to implement Iterator right? Yes, we could do that, but that would limit us in certain ways.
Instead we create an intermediate "iterable" structure and implement the Iterator trait:
// NOTE: I have removed all lifetimes to make it less messy.
struct ListIter<T> { cursor: &List<T> };
impl<T> Iterator for ListIter<T> {
type Item = &T;
fn next(&mut self) -> Option<Self::Item> {...}
}
So now we need to somehow connect List<T> and ListIter<T>. This can be done by implementing IntoIterator for List.
impl<T> IntoIterator for List<T> {
type Item = T;
type Iter = ListIter<Self::Item>;
fn into_iter(self) -> Self::Iter { ListIter { cursor: &self } }
}
IntoIterator can also be implemented multiple times for container struct if for example it contains different nested iterable fields or we have some higher kinded type situation.
Lets say we have a Collection<T>: IntoIterator trait that will be implemented by multiple data structures, e.g. List<T>, Vector<T> and Tree<T> that also have their respective Iter; ListIter<T>, VectorIter<T> and TreeIter<T>. But what does this actually mean when we go from generic to specific code?
fn wrapper<C>(value: C) where C: Collection<i32> {
let iter = value.into_iter() // But what iterator are we?
...
}
This code is not 100% correct, lifetimes and mutability support are omitted.
This question already has answers here:
is it possible to filter on a vector in-place?
(4 answers)
Closed 3 years ago.
I've started working through the Project Euler problems in Rust and came across #3 where the easiest quick-approach would be to implement a Sieve of Eratosthenes.
In doing so, my algorithm creates an iterator to then filter non-primes out of and assign it back to the original vector, but I'm receiving an error that Vec<u32> can't be built from Iterator<Item=&u32>.
Code:
fn eratosthenes_sieve(limit: u32) -> Vec<u32> {
let mut primes: Vec<u32> = Vec::new();
let mut range: Vec<u32> = (2..=limit).collect();
let mut length = range.len();
loop {
let p = range[0];
primes.push(p);
range = range.iter().filter(|&n| *n % p != 0).collect();
if length == range.len() {
break;
}
length = range.len();
}
primes
}
Error:
error[E0277]: a collection of type `std::vec::Vec<u32>` cannot be built from an iterator over elements of type `&u32`
--> src\main.rs:42:55
|
42 | range = range.iter().filter(|&n| *n % p != 0).collect();
| ^^^^^^^ a collection of type `std::vec::Vec<u32>` cannot be built from `std::iter::Iterator<Item=&u32>`
|
= help: the trait `std::iter::FromIterator<&u32>` is not implemented for `std::vec::Vec<u32>`
Why is the closure wrapping the values in extra borrows?
Explanation
According to the error message, the expression range.iter().filter(|&n| *n % p != 0) is an iterator over items of type &u32: a reference to an u32. You expected an iterator over u32 by value. So let's walk backwards:
The filter(...) part of the iterator chain has actually nothing to do with your problem. When we take a look at Iterator::filter, we see that it returns Filter<Self, P>. This type implements Iterator:
impl<I: Iterator, P> Iterator for Filter<I, P>
where
P: FnMut(&I::Item) -> bool,
{
type Item = I::Item;
// ...
}
The important part here is that type Item = I::Item, meaning that the item type of I (the original iterator) is passed through exactly. No reference is added.
This leaves .iter(): that's the cause of the problem. Vec::iter returns slice::Iter<T> which implements Iterator:
impl<'a, T> Iterator for Iter<'a, T> {
type Item = &'a T;
// ...
}
And here we see that the item type is a reference to T (the element type of the vector).
Solutions
In general cases, you could call .cloned() on any iterator that iterates over references to get a new iterator that iterates over the items by value (by cloning each item). For types that implement Copy you can (and should) use .copied(). E.g. range.iter().filter(|&n| *n % p != 0).copied().collect().
However, in this case there is a better solution: since you don't need the vector afterwards anymore, you can just call into_iter() instead of iter() in order to directly get an iterator over u32 by value. This consumes the vector, making it inaccessible afterwards. But, as said, that's not a problem here.
range = range.into_iter().filter(|&n| n % p != 0).collect();
Also note that I removed the * in *n, as the dereference is not necessary anymore.
Other hints
Always reallocating a new vector is not very fast. The Sieve of Eratosthenes is classically implemented in a different way: instead of storing the numbers, one only stores Booleans to denote for each number if it's prime or not. The numbers are never stored explicitly, but implicitly by using the indices of the vector/array.
And to make it really fast, one should not use a Vec<bool> but instead a dedicated bitvec. Vec<bool> stores one byte per bool, although only one bit would be necessary. The de-facto crate that offers such a bit vector is bit-vec, which conveniently also shows an example implementation of Sieve of Eratosthenes in its documentation.
I am new to Rust. I need to create a vector before a for loop. Run for loop on it. Change the vector inside the for loop. Then Change the vector after the for loop.
I tried the following code and tried to use immutable borrow but both did not work.
fn main() {
let mut vec1 = vec![4, 5];
vec1.push(6);
for i in vec1 {
if i % 2 == 0 {
vec1.push(7);
}
}
vec1.push(8);
println!("vec1={:?}", vec1);
}
I expect to compile and change the vector inside and after the for loop. But it shows this error message:
error[E0382]: borrow of moved value: `vec1`
--> src/main.rs:6:13
|
2 | let mut vec1 = vec![4, 5];
| -------- move occurs because `vec1` has type `std::vec::Vec<i32>`, which does not implement the `Copy` trait
3 | vec1.push(6);
4 | for i in vec1 {
| ----
| |
| value moved here
| help: consider borrowing to avoid moving into the for loop: `&vec1`
5 | if i % 2 == 0 {
6 | vec1.push(7);
| ^^^^ value borrowed here after move
Can you explain why move occurs? Can you make it compile?
There are two problems with your code. Luckily, both of them can be solved by changing one thing. The problems are:
Writing for _ in vec1 moves the value vec1 into the loop. This is a move like every other move, meaning that the value is not accessible after the loop! If you need a refresher on ownership and moving, please read the related chapter in the Rust book. You can iterating over references to a vector's elements via for _ in &vec1.
You are trying to mutate the vector you are iterating over. Regardless of whether you iterate over the vector by value (as you do) or by reference, you cannot change the vector while iterating over it. And there are many good reasons for that! In your case, you could easily end up having an infinite loop if you add elements while iterating.
To solve both problems at the same time, you can iterate over indices to the vector instead of the vectors elements (Playground):
let mut vec1 = vec![4, 5];
vec1.push(6);
for i in 0..vec1.len() {
if vec1[i] % 2 == 0 {
vec1.push(7);
}
}
vec1.push(8);
println!("vec1={:?}", vec1);
This way, the vector is not moved nor borrowed by the for loop and we are free to mutate it during and after the loop. This particular solution iterates over the indices of the original vector, meaning that elements added in the loop are not iterated over by the loop. This is a good protection to avoid accidental infinite loops. Please note, however, that you can still shoot yourself in the foot by, for example, removing elements from the vector during iteration. In general, independent of programming language, mutating collections while iterating over them is dangerous and should only be done with care.
you can do it with loop like this, but be careful.(Playground)
fn main() {
let mut vec1 = vec![4, 5];
vec1.push(6);
let mut i = 0;
loop {
if i >= vec1.len() { break; }
if vec1[i] % 2 == 0 {
vec1.push(7);
}
i += 1;
}
vec1.push(8);
println!("vec1={:?}", vec1);
}
This question already has answers here:
What is the exact definition of the for loop in Rust?
(2 answers)
What does it mean to pass in a vector into a `for` loop versus a reference to a vector?
(1 answer)
What is the difference between iter and into_iter?
(5 answers)
Closed 4 years ago.
This aspect of Rust seems rather bizarre at first, and I'd like to know how for typing works in this case.
fn sum1(v: &mut Vec<i32>) -> i32 {
// I know I don't mutate v, but that's not the point
let mut s = 0;
for e in v {
s += *e;
// s += e; <- Err!
}
s
}
fn sum2(v: &Vec<i32>) -> i32 {
let mut s = 0;
for e in v {
s += e; // Okay?
}
s
}
fn main() {
let mut v = vec![1, 2, 3];
println!("{}", sum1(&mut v));
println!("{}", sum2(&v));
}
At first I was confused as to why it wanted me to dereference in sum1. But okay, it's saying AddAssign<&mut i32> is not implemented, so I see that e is &mut i32. That's really awesome for changing e on the fly.
But now, with that information, why does sum2 compile? It seems like AddAssign<&i32> is indeed implemented (Which I tested separately), so it's possible that it's using that AddAssign method on a &i32, but then again it's also possible that e is simply i32. My question boils down to:
Is e an i32, or an &i32?
I don't know enough about references to easily check the difference myself (By using something other than i32 and checking for crashes). &i32 is the most logical but I would like to know for sure.
Coming from C, mixing between pointers and values is extremely confusing, and it has already taken me quite a bit to get used to with functions automatically dereferencing/referencing their arguments (which I don't think I've really done yet).
EDIT:
The type could be verified (And answer my question) with How do I print the type of a variable in Rust?, which is useful to mess around with but doesn't directly answer how for loops work. What is the exact definition of the for loop in Rust? helps a lot, and What is the difference between iter and into_iter? talks about a related IntoIterator trait that can cause for loop confusion.