TL; DR:
- Итератора , возвращенное
into_iter
может давать какой - либо из T
, &T
или &mut T
, в зависимости от контекста.
- Итератор, возвращаемый функцией,
iter
будет выдан &T
по соглашению.
- Итератор, возвращаемый функцией,
iter_mut
будет выдан &mut T
по соглашению.
Первый вопрос: «Что есть into_iter
?»
into_iter
происходит от IntoIterator
черты :
pub trait IntoIterator
where
<Self::IntoIter as Iterator>::Item == Self::Item,
{
type Item;
type IntoIter: Iterator;
fn into_iter(self) -> Self::IntoIter;
}
Вы реализуете эту особенность, когда хотите указать, как конкретный тип должен быть преобразован в итератор. В частности, если тип реализует IntoIterator
его, он может использоваться в for
цикле.
Например, Vec
реализует IntoIterator
... трижды!
impl<T> IntoIterator for Vec<T>
impl<'a, T> IntoIterator for &'a Vec<T>
impl<'a, T> IntoIterator for &'a mut Vec<T>
Каждый вариант немного отличается.
Этот использует Vec
и его итератор выдает значения ( T
напрямую):
impl<T> IntoIterator for Vec<T> {
type Item = T;
type IntoIter = IntoIter<T>;
fn into_iter(mut self) -> IntoIter<T> { /* ... */ }
}
Два других берут вектор по ссылке (не обманывайте себя сигнатурой, into_iter(self)
потому self
что в обоих случаях это ссылка), и их итераторы будут создавать ссылки на элементы внутри Vec
.
Это дает неизменные ссылки :
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> { /* ... */ }
}
В то время как этот дает изменяемые ссылки :
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> { /* ... */ }
}
Так:
В чем разница между iter
и into_iter
?
into_iter
является общим методом для получения итератора, независимо от того, выдает ли этот итератор значения, неизменяемые или изменяемые ссылки, зависит от контекста и иногда может вызывать удивление.
iter
и iter_mut
являются специальными методами. Поэтому их возвращаемый тип не зависит от контекста и обычно будет итераторами, дающими неизменяемые и изменяемые ссылки соответственно.
Автор поста Rust by Example иллюстрирует удивление, into_iter
вызванное зависимостью от контекста (т. Е. Типа), который вызывается, а также усугубляет проблему, используя тот факт, что:
IntoIterator
не реализовано [T; N]
, только для &[T; N]
и&mut [T; N]
- Когда метод не реализован для значения, он автоматически ищет ссылки на это значение
что очень удивительно, into_iter
поскольку все типы (кроме [T; N]
) реализуют его для всех трех вариантов (значение и ссылки). Массив не может реализовать итератор, который выдает значения, потому что он не может «сжаться», чтобы отказаться от своих элементов.
Относительно того, почему массивы реализуются IntoIterator
(таким удивительным образом): это позволяет делать итерации по ссылкам на них в for
циклах.
into_iter
выбирает реализацию на основе того, является ли получатель значением, ссылкой или изменяемой ссылкой. (2) В Rust нет изменяемых значений, точнее, любое значение является изменяемым, поскольку у вас есть право собственности.&'a MyStruct
и&mut 'a MyStruct
и первый один всегда был выбран , если присутствует , даже если я позвонюinto_iter().for_each()
поmut
значению с&mut
аргументами в лямбда.Я (новичок в Rust) пришел сюда из Google в поисках простого ответа, который не был предоставлен другими ответами. Вот этот простой ответ:
iter()
перебирает предметы по ссылкеinto_iter()
перебирает предметы, перемещая их в новую область видимостиiter_mut()
перебирает элементы, давая изменчивую ссылку на каждый элементТак
for x in my_vec { ... }
что по сути эквивалентноmy_vec.into_iter().for_each(|x| ... )
- обаmove
элементаmy_vec
входят в...
сферу.Если вам просто нужно «посмотреть» на данные, используйте
iter
, если вам нужно отредактировать / изменить их, используйтеiter_mut
, и если вам нужно дать новому владельцу, используйтеinto_iter
.Это было полезно: http://hermanradtke.com/2015/06/22/effectively-using-iterators-in-rust.html
Сделать это вики-сообществом, чтобы, надеюсь, профессионал Rust мог редактировать этот ответ, если я допустил какие-либо ошибки.
источник
iter
иinto_iter
..into_iter()
не реализован для самого массива, но только&[]
. Для сравнения:с участием
Так
IntoIterator
как определяется только на&[T]
, сам срез не может быть отброшен так же, какVec
при использовании значений. (значения не могут быть перемещены)Теперь, почему это так, это другие вопросы, и я хотел бы узнать сам. Предположение: массив - это сами данные, срез - это только просмотр. На практике вы не можете переместить массив как значение в другую функцию, просто передайте его представление, поэтому вы не сможете использовать его и там.
источник
IntoIterator
также реализован для&'a mut [T]
, так что он может перемещать объекты из массива. Я думаю, что это связано с тем фактом, что возвращаемая структураIntoIter<T>
не имеет аргумента времени жизни, аIter<'a, T>
первый не может содержать фрагмент.mut
означает, что вы можете изменить значения, а не вывести их.let mut a = ["abc".to_string()]; a.into_iter().map(|x| { *x });
=> «ошибка: невозможноArrayIntoIter
структуру, использующую небезопасный Rust, как часть библиотеки ... Может быть, это того не стоит, так как вVec
любом случае вы должны использовать его в этих случаях.array.into_iter
возвращается&T
- потому что он делает магию, чтобы автоматически преобразовать его в&array.into_iter
- и если так, я не понимаю, что это имеет отношение к движущимся или не движущимся значениям. Или, как сказал @rodrigo, вы получаете ссылку просто потому, что (по какой-то причине) вы не можете перемещать значения из массивов ? Все еще очень смущен.Я думаю, есть кое-что, чтобы уточнить немного больше. Типы коллекций, такие как
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
метод (т. Е. Он является итеративным), используя границу признака:Однако мы не можем * использовать признак, связанный с требованием, чтобы у типа был
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>
:Если
v.iter()
эквивалентно тому,&v
что оба реализуютIntoIterator<Item=&T>
, почему тогда Rust предоставляет оба? Это для эргономики. Вfor
циклах это немного более сжато,&v
чемv.iter()
; но в других случаяхv.iter()
это намного понятнее, чем(&v).into_iter()
:Аналогично, в
for
циклахv.iter_mut()
можно заменить на&mut v
:Когда предоставлять (реализовывать)
into_iter
иiter
методы для типаЕсли у типа есть только один «путь» для итерации, мы должны реализовать оба. Однако, если есть два или более способов, по которым он может повторяться, мы должны вместо этого предоставить специальный метод для каждого способа.
Например, не
String
предоставляет ни то,into_iter
ни другое,iter
потому что существует два способа итерировать его: итерировать его представление в байтах или итерировать его представление в символах. Вместо этого он предоставляет два метода:bytes
для итерации байтов иchars
для итерации символов в качестве альтернативыiter
методу.* Ну, технически мы можем сделать это, создав черту. Но тогда нам нужно
impl
эту черту для каждого типа, который мы хотим использовать. Между тем многие типыstd
уже реализованыIntoIterator
.источник