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.