Я думаю, есть кое-что, чтобы уточнить немного больше. Типы коллекций, такие как Vec<T>
и VecDeque<T>
, имеют into_iter
метод, который возвращает, T
потому что они реализуют IntoIterator<Item=T>
. Нет ничего, что могло бы помешать нам создать тип, Foo<T>
если он повторяется, это даст не T
другой тип U
. То есть Foo<T>
реализует IntoIterator<Item=U>
.
На самом деле, есть несколько примеров std
: &Path
реализует IntoIterator<Item=&OsStr>
и &UnixListener
реализует IntoIterator<Item=Result<UnixStream>>
.
Разница между into_iter
иiter
Вернемся к исходному вопросу о разнице между into_iter
и iter
. Подобно тому, что указывали другие, разница в том, что into_iter
это обязательный метод, IntoIterator
который может давать любой тип, указанный в IntoIterator::Item
. Как правило, если тип реализует IntoIterator<Item=I>
, по соглашению он также имеет два специальных метода: iter
и iter_mut
которые дают &I
и &mut I
, соответственно.
Это означает, что мы можем создать функцию, которая получает тип, у которого есть into_iter
метод (т. Е. Он является итеративным), используя границу признака:
fn process_iterable<I: IntoIterator>(iterable: I) {
for item in iterable {
// ...
}
}
Однако мы не можем * использовать признак, связанный с требованием, чтобы у типа был iter
метод или iter_mut
метод, потому что это просто соглашения. Можно сказать, что into_iter
это более широко применимо, чем iter
или iter_mut
.
Альтернативы iter
иiter_mut
Еще один интересный момент для наблюдения - это iter
не единственный способ получить итератор, который дает результат &T
. По соглашению (снова), типы коллекций , SomeCollection<T>
в std
которых есть iter
метод также их неизменные ссылочные типы &SomeCollection<T>
реализации IntoIterator<Item=&T>
. Например, &Vec<T>
реализует IntoIterator<Item=&T>
, что позволяет нам перебирать &Vec<T>
:
let v = vec![1, 2];
// Below is equivalent to: `for item in v.iter() {`
for item in &v {
println!("{}", item);
}
Если v.iter()
эквивалентно тому, &v
что оба реализуют IntoIterator<Item=&T>
, почему тогда Rust предоставляет оба? Это для эргономики. В for
циклах это немного более сжато, &v
чем v.iter()
; но в других случаях v.iter()
это намного понятнее, чем (&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();
Аналогично, в for
циклах v.iter_mut()
можно заменить на &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;
}
Когда предоставлять (реализовывать) into_iter
и iter
методы для типа
Если у типа есть только один «путь» для итерации, мы должны реализовать оба. Однако, если есть два или более способов, по которым он может повторяться, мы должны вместо этого предоставить специальный метод для каждого способа.
Например, не String
предоставляет ни то, into_iter
ни другое, iter
потому что существует два способа итерировать его: итерировать его представление в байтах или итерировать его представление в символах. Вместо этого он предоставляет два метода: bytes
для итерации байтов и chars
для итерации символов в качестве альтернативы iter
методу.
* Ну, технически мы можем сделать это, создав черту. Но тогда нам нужно impl
эту черту для каждого типа, который мы хотим использовать. Между тем многие типы std
уже реализованы IntoIterator
.