Я думаю, що є щось, щоб уточнити трохи більше. Типи колекцій, такі як 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()
рівнозначно , чому тоді Rust надає обоє? Це для ергономіки. У циклах використовувати трохи більш стисло, ніж ; але в інших випадках набагато зрозуміліше, ніж :&v
IntoIterator<Item=&T>
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
.