İter ve into_iter arasındaki fark nedir?


175

Rust bu kod parçacığı olan örnek öğretici ile yapıyorum :

// 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));

Ben tamamen karıştı - için bir Vecyineleyici iterverim başvuruları döndü ve yineleyici into_iterverim değerleri döndü , ama bir dizi için bu yineleyiciler aynı?

Bu iki yöntem için kullanım durumu / API nedir?

Yanıtlar:


147

TL; DR:

  • Tarafından döndürülen yineleyici into_iterherhangi verebilir T, &Tveya&mut T bağlama bağlı olarak,.
  • Tarafından döndürülen yineleyici , konvansiyon ile iterverim verecektir &T.
  • Tarafından döndürülen yineleyici , konvansiyon ile iter_mutverim verecektir &mut T.

İlk soru: "Nedir into_iter?"

into_iterIntoIteratorözellikten gelir :

pub trait IntoIterator 
where
    <Self::IntoIter as Iterator>::Item == Self::Item, 
{
    type Item;
    type IntoIter: Iterator;
    fn into_iter(self) -> Self::IntoIter;
}

Bu özelliği, belirli bir türün yineleyiciye nasıl dönüştürüleceğini belirtmek istediğinizde uygularsınız. En önemlisi, eğer bir tip uygulanmışsa IntoIterator, bir fordöngü içinde kullanılabilir .

Örneğin, Vecuygular IntoIterator... üç kez!

impl<T> IntoIterator for Vec<T>
impl<'a, T> IntoIterator for &'a Vec<T>
impl<'a, T> IntoIterator for &'a mut Vec<T>

Her varyant biraz farklıdır.

Bu tüketir Vecve yineleyicisi ( doğrudan) değerler verirT :

impl<T> IntoIterator for Vec<T> {
    type Item = T;
    type IntoIter = IntoIter<T>;

    fn into_iter(mut self) -> IntoIter<T> { /* ... */ }
}

Diğer iki referans olarak vektörü almak (imza ile aldanmayın into_iter(self)çünkü selfve yineleyiciler elemanları iç başvurular üretecek her iki durumda da bir referanstır)Vec .

Bu değişmez referanslar verir :

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> { /* ... */ }
}

Bu bir iken verir değişken başvuruları :

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> { /* ... */ }
}

Yani:

Arasındaki fark nedir iterve into_iter?

into_iterbu yineleyicinin değerler, değişmez referanslar veya değişebilir referanslar verip vermediği bir yineleyici elde etmek için genel bir yöntemdir , bağlama bağlıdır ve bazen şaşırtıcı olabilir.

iter ve iter_mut geçici yöntemlerdir. Dolayısıyla geri dönüş tipleri bağlamdan bağımsızdır ve geleneksel olarak sırasıyla değişmez referanslar ve değişebilir referanslar veren yineleyiciler olacaktır.

Örnek post tarafından Rust'un yazarı into_iter, çağrılan bağlama (yani, türe) bağımlılıktan gelen sürprizi gösterir ve ayrıca şu gerçeği kullanarak sorunu birleştirir:

  1. IntoIteratoriçin değil [T; N], yalnızca &[T; N]ve&mut [T; N]
  2. Bir yöntem bir değer için uygulanmadığında, bunun yerine otomatik olarak bu değere yapılan başvuruları arar

Bu, into_iterher tür (hariç [T; N]) tüm 3 varyasyon (değer ve referanslar) için uyguladığından çok şaşırtıcıdır . Dizinin, değer veren bir yineleyici uygulaması mümkün değildir, çünkü öğelerinden vazgeçmek için "küçülemez".

Dizilerin neden IntoIterator(şaşırtıcı bir şekilde) uygulandığına gelince : fordöngülerde bunlara yapılan referansları yinelemeyi mümkün kılmak .


14
Bu blog gönderisini yararlı buldum: hermanradtke.com/2015/06/22/…
poy

> bu yineleyicinin değerler, değişmez referanslar veya değişebilir referanslar sağlayıp sağlamadığı bağlama bağlıdır mı? Örneğin iter_mut'u değişebilir değerler vermeye zorlar mıydı?
Dan M.

@DanM .: (1) into_iterAlıcının bir değer, referans veya değiştirilebilir referans olup olmadığına bağlı olarak bir uygulamayı seçtiği anlamına gelir . (2) Rust'da değiştirilebilir değerler yoktur veya daha ziyade sahipliğiniz olduğu için herhangi bir değer değiştirilebilir.
Matthieu M.

@ MatthieuM.hm, testlerimde durum böyle görünmüyor. Ben IntoIter için uyguladım &'a MyStructve lambda argümanları ile değer &mut 'a MyStructçağırdı bile ilk varsa her zaman seçildi . into_iter().for_each()mut&mut
Dan M.

1
@Ixx: Teşekkürler, bu çok faydalı. Bir TL sağlamaya karar verdim; cevabın ortasına gömülmemesi için sorunun en üstünde DR, ne düşünüyorsunuz?
Matthieu M.

78

Ben (bir Rust acemi) Google'dan, diğer cevaplar tarafından sağlanmayan basit bir cevap aramaya geldim. İşte bu basit cevap:

  • iter() referans ile öğeler üzerinde tekrarlar
  • into_iter() öğeler üzerinde yineleyerek yeni kapsama taşıyor
  • iter_mut() her öğeye değiştirilebilir bir referans vererek öğeler üzerinde yineleme yapar

Yani for x in my_vec { ... }esasen eşittir my_vec.into_iter().for_each(|x| ... )ikisi de - moveunsurları my_veciçine ...kapsamı.

Verilere sadece "bakmanız" gerekiyorsa, kullanın iter, düzenlemeniz / değiştirmeniz gerekiyorsa, kullanın iter_mutve yeni bir sahip vermeniz gerekiyorsa kullanın into_iter.

Bu yardımcı oldu: http://hermanradtke.com/2015/06/22/efftively-using-iterators-in-rust.html

Bunu bir topluluk wiki yapmak, böylece bir Rust profesyonelinin herhangi bir hata yapmışsam bu yanıtı düzenleyebileceğini umuyoruz.


7
Teşekkür ederim ... Kabul edilen cevabın iterve ile arasındaki farkı nasıl ifade ettiğini görmek zor into_iter.
mmw

Tam da aradığım şey buydu!
Mart'ta Cyrusmith

6

.into_iter()bir dizinin kendisi için değil, yalnızca bir dizi için uygulanır &[]. Karşılaştırmak:

impl<'a, T> IntoIterator for &'a [T]
    type Item = &'a T

ile

impl<T> IntoIterator for Vec<T>
    type Item = T

Yana IntoIteratorsadece tanımlanır &[T], dilim kendisi aynı şekilde olarak bırakılan olamazVec değerleri kullandığınızda. (değerler taşınamaz)

Şimdi, durum neden farklı bir konudur ve kendimi öğrenmek istiyorum. Spekülasyon: dizi verinin kendisidir, dilim sadece bir görünümdür. Pratikte diziyi bir değer olarak başka bir işleve taşıyamazsınız, sadece bir görünümünü geçirirsiniz, böylece onu da tüketemezsiniz.


IntoIteratorAyrıca için uygulanan &'a mut [T]çok, olabilir dizisinin üzerinden nesneleri hareket. Bence, dönüş yapısının IntoIter<T>ömür boyu bir argümana sahip olmadığı gerçeği ile ilgili olduğunu düşünüyorum, bu Iter<'a, T>yüzden eski bir dilim tutamaz.
rodrigo

mutbu, değerleri değiştirebileceğiniz anlamına gelir, bunları taşıyamayacağınız anlamına gelmez.
viraptor

@rodrigo let mut a = ["abc".to_string()]; a.into_iter().map(|x| { *x });=> "hata: ödünç alınan içeriğin dışına taşınamaz"
viraptor

Evet, haklısın ve değerler diziden taşınamaz. Bununla birlikte, ArrayIntoIterkütüphanenin bir parçası olarak güvensiz Rust kullanarak bir tür yapının mümkün olabileceğini düşünüyorum ... Belki de buna değmez, Vecyine de bu durumlar için kullanmalısınız .
rodrigo

bu yüzden anlamıyorum ... array.into_itergeri dönmesinin nedeni &T- otomatik olarak dönüştürmek için sihir &array.into_iteryapmaktır - ve eğer öyleyse, bu değerlerin hareketli değerlerle veya hareketli değerlerle ne ilgisi olduğunu anlamıyorum. Ya da @rodrigo'nun dediği gibi, referansı basitçe (bir nedenden dolayı) dizilerden dışarı çıkaramayacağınız için mi alıyorsunuz ? Hala çok karışık.
vitiral

2

Bence biraz daha açıklığa kavuşacak bir şey var. Koleksiyon türleri, Vec<T>ve gibi VecDeque<T>, uyguladıkları için into_iterverim yöntemine sahiptir . Tekrarlanan bir tür yaratmamızı durduracak hiçbir şey yok , başka bir tür vermez . Yani, uygular .TIntoIterator<Item=T>Foo<T>TUFoo<T>IntoIterator<Item=U>

Aslında, bazı örnekler vardır std: &Path uygular IntoIterator<Item=&OsStr> ve &UnixListener uygular IntoIterator<Item=Result<UnixStream>> .


Arasındaki fark into_iterveiter

Arasındaki farkı asıl soruya geri dön into_iterve iter. Diğerlerinin belirttiği gibi, fark, belirtilen herhangi bir tip verebilen into_itergerekli bir yöntem olmasıdır . Tipik olarak, eğer bir tip uygularsa , konvansiyonla aynı zamanda iki ad-hoc yöntemi vardır: ve sırasıyla ve verim .IntoIteratorIntoIterator::ItemIntoIterator<Item=I>iteriter_mut&I&mut I

Bunun anlamı, into_iterbağlı bir özellik kullanarak yöntemi olan (yani yinelenebilir) bir tür alan bir işlev oluşturabilmemizdir :

fn process_iterable<I: IntoIterator>(iterable: I) {
    for item in iterable {
        // ...
    }
}

Ancak, yöntem veya yöntem için bir tür gerektiren bir özelliği * kullanamayız , çünkü bunlar yalnızca konvansiyonlardır. Bunun veya ' dan daha yaygın olarak kullanılabilir olduğunu söyleyebiliriz .iteriter_mutinto_iteriteriter_mut

Alternatifler iterveiter_mut

Gözlemlenmesi gereken bir başka ilginç şey ise iter , bir yineleyici elde etmenin tek yolunun olmamasıdır &T. Geleneksel olarak (tekrar), toplama türleri SomeCollection<T>içinde stdvar olan iteryöntem de onların değişmez referans tipleri vardır &SomeCollection<T>uygulamak IntoIterator<Item=&T>. Örneğin, &Vec<T> uygular IntoIterator<Item=&T> , böylece tekrarlamamızı sağlar &Vec<T>:

let v = vec![1, 2];

// Below is equivalent to: `for item in v.iter() {`
for item in &v {
    println!("{}", item);
}

Her iki uygulamada v.iter()da eşdeğerse , Rust neden her ikisini de sağlar? Ergonomi için. Gelen döngüler, bu kullanımda biraz daha özlü var daha ; ancak diğer durumlarda, aşağıdakilerden çok daha açıktır :&vIntoIterator<Item=&T>for&vv.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();

Benzer şekilde, fordöngülerde v.iter_mut()şu ile değiştirilebilir &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;
}

Ne zaman sağlanmalı (uygulanmalı) into_iterveiter yöntemler

Türün yinelenecek tek bir “yolu” varsa, her ikisini de uygulamalıyız. Ancak, yinelenebilecek iki veya daha fazla yol varsa, bunun yerine her bir yol için geçici bir yöntem sağlamalıyız.

Örneğin, String yinelemenin iki yolu olmadığı için into_iterne sağlar ne de sağlar iter: bayt cinsinden temsilini yinelemek veya karakterdeki temsilini yinelemek. Bunun yerine, iki yöntem sunar: yönteme alternatif olarak bytesbaytları charsyinelemek ve karakterleri yinelemek için iter.


* Teknik olarak bunu bir özellik oluşturarak yapabiliriz. Ama sonra implkullanmak istediğimiz her tür için bu özelliğe ihtiyacımız var . Bu arada, birçok türü stdzaten uygulamak IntoIterator.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.