TL; DR:
- Iterator yang dikembalikan oleh
into_iter
dapat menghasilkan T
, &T
atau &mut T
tergantung pada konteksnya.
- Iterator yang dikembalikan oleh
iter
akan menghasilkan &T
, dengan konvensi.
- Iterator yang dikembalikan oleh
iter_mut
akan menghasilkan &mut T
, dengan konvensi.
Pertanyaan pertama adalah: "Apa itu into_iter
?"
into_iter
berasal dari IntoIterator
sifat :
pub trait IntoIterator
where
<Self::IntoIter as Iterator>::Item == Self::Item,
{
type Item;
type IntoIter: Iterator;
fn into_iter(self) -> Self::IntoIter;
}
Anda menerapkan sifat ini ketika Anda ingin menentukan bagaimana tipe tertentu akan dikonversi menjadi iterator. Terutama, jika suatu tipe mengimplementasikannya IntoIterator
dapat digunakan dalam satu for
lingkaran.
Misalnya, Vec
menerapkan IntoIterator
... tiga kali!
impl<T> IntoIterator for Vec<T>
impl<'a, T> IntoIterator for &'a Vec<T>
impl<'a, T> IntoIterator for &'a mut Vec<T>
Setiap varian sedikit berbeda.
Ini mengkonsumsi Vec
dan iteratornya menghasilkan nilai ( T
langsung):
impl<T> IntoIterator for Vec<T> {
type Item = T;
type IntoIter = IntoIter<T>;
fn into_iter(mut self) -> IntoIter<T> { /* ... */ }
}
Dua lainnya mengambil vektor dengan referensi (jangan tertipu oleh tanda tangan into_iter(self)
karena self
merupakan referensi dalam kedua kasus) dan iterator mereka akan menghasilkan referensi ke elemen di dalamnya Vec
.
Yang ini menghasilkan referensi abadi :
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> { /* ... */ }
}
Sementara ini menghasilkan referensi yang bisa berubah :
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> { /* ... */ }
}
Begitu:
Apa perbedaan antara iter
dan into_iter
?
into_iter
adalah metode generik untuk mendapatkan iterator, apakah iterator ini menghasilkan nilai, referensi yang tidak dapat diubah atau referensi yang dapat diubah tergantung pada konteks dan terkadang mengejutkan.
iter
dan iter_mut
merupakan metode ad-hoc. Jenis pengembalian mereka karena itu independen dari konteks, dan secara konvensional akan menjadi iterator yang menghasilkan referensi yang tidak berubah dan referensi yang dapat berubah, masing-masing.
Penulis tulisan Rust by Example mengilustrasikan kejutan yang datang dari ketergantungan pada konteks (yaitu, tipe) yang into_iter
dipanggil, dan juga menambah masalah dengan menggunakan fakta bahwa:
IntoIterator
tidak diterapkan untuk [T; N]
, hanya untuk &[T; N]
dan&mut [T; N]
- Ketika suatu metode tidak diterapkan untuk suatu nilai, itu secara otomatis mencari referensi ke nilai itu sebagai gantinya
yang sangat mengejutkan into_iter
karena semua tipe (kecuali [T; N]
) mengimplementasikannya untuk ketiga variasi (nilai dan referensi). Array tidak mungkin untuk mengimplementasikan iterator yang menghasilkan nilai karena ia tidak dapat "menyusut" untuk menyerahkan item-itemnya.
Seperti mengapa array mengimplementasikan IntoIterator
(dengan cara yang mengejutkan): itu memungkinkan untuk beralih pada referensi ke mereka dalam for
loop.
into_iter
memilih implementasi berdasarkan pada apakah penerima adalah nilai, referensi atau referensi yang bisa berubah. (2) Tidak ada nilai yang bisa berubah di Rust, atau lebih tepatnya, nilai apa pun bisa berubah karena Anda memiliki kepemilikan.&'a MyStruct
dan&mut 'a MyStruct
dan yang pertama selalu dipilih jika ada bahkan jika aku meneleponinto_iter().for_each()
padamut
nilai dengan&mut
argumen di lambda.Saya (seorang pemula Rust) datang ke sini dari Google mencari jawaban sederhana yang tidak disediakan oleh jawaban lain. Inilah jawaban sederhana itu:
iter()
beralih pada item dengan referensiinto_iter()
beralih ke item, memindahkannya ke ruang lingkup baruiter_mut()
beralih pada item, memberikan referensi yang bisa berubah untuk setiap itemJadi
for x in my_vec { ... }
pada dasarnya setara denganmy_vec.into_iter().for_each(|x| ... )
- keduamove
elemen yangmy_vec
masuk ke dalam...
ruang lingkup.Jika Anda hanya perlu "melihat" data, gunakan
iter
, jika Anda perlu mengedit / mengubahnya, gunakaniter_mut
, dan jika Anda perlu memberikannya kepada pemilik baru, gunakaninto_iter
.Ini sangat membantu: http://hermanradtke.com/2015/06/22/effectively-using-iterators-in-rust.html
Jadikan ini sebagai wiki komunitas sehingga semoga pro karat dapat mengedit jawaban ini jika saya membuat kesalahan.
sumber
iter
daninto_iter
..into_iter()
tidak diimplementasikan untuk array itu sendiri, tetapi hanya&[]
. Membandingkan:dengan
Karena
IntoIterator
didefinisikan hanya pada&[T]
, irisan itu sendiri tidak dapat dijatuhkan dengan cara yang sama sepertiVec
ketika Anda menggunakan nilai-nilai. (nilai tidak dapat dipindahkan)Sekarang, mengapa itu menjadi masalah yang berbeda, dan saya ingin belajar sendiri. Berspekulasi: array adalah data itu sendiri, slice hanya pandangan ke dalamnya. Dalam praktiknya Anda tidak bisa memindahkan array sebagai nilai ke fungsi lain, cukup lewati saja, jadi Anda juga tidak bisa mengkonsumsinya.
sumber
IntoIterator
juga diimplementasikan untuk&'a mut [T]
, sehingga bisa memindahkan objek dari array. Saya pikir itu terkait dengan fakta bahwa struct kembaliIntoIter<T>
tidak memiliki argumen seumur hidupIter<'a, T>
, jadi yang pertama tidak dapat memegang sepotong.mut
berarti Anda dapat mengubah nilai, bukan berarti Anda bisa memindahkannya.let mut a = ["abc".to_string()]; a.into_iter().map(|x| { *x });
=> "error: tidak bisa keluar dari konten yang dipinjam"ArrayIntoIter
struct menggunakan Rust yang tidak aman, sebagai bagian dari perpustakaan ... Mungkin tidak sepadan, seperti yang seharusnya Anda gunakanVec
untuk kasus-kasus itu.array.into_iter
mengembalikan&T
- karena melakukan sulap untuk mengubahnya secara otomatis&array.into_iter
- dan jika demikian, saya tidak mengerti apa yang harus dilakukan dengan nilai bergerak atau tidak nilai bergerak. Atau apakah seperti yang dikatakan @rodrigo, bahwa Anda mendapatkan referensi hanya karena (karena alasan tertentu) Anda tidak dapat memindahkan nilai dari array ? Masih sangat bingung.Saya pikir ada sesuatu yang perlu diklarifikasi sedikit lagi. Jenis koleksi, seperti
Vec<T>
danVecDeque<T>
, memilikiinto_iter
metode yang menghasilkanT
karena mereka menerapkanIntoIterator<Item=T>
. Tidak ada yang menghentikan kita untuk membuat tipeFoo<T>
jika yang diulangi, itu akan menghasilkan bukanT
tipe lainU
. Yaitu,Foo<T>
implementasinyaIntoIterator<Item=U>
.Bahkan, ada beberapa contoh di
std
:&Path
alatIntoIterator<Item=&OsStr>
dan&UnixListener
alatIntoIterator<Item=Result<UnixStream>>
.Perbedaan antara
into_iter
daniter
Kembali ke pertanyaan awal tentang perbedaan antara
into_iter
daniter
. Serupa dengan yang ditunjukkan orang lain, perbedaannya adalahinto_iter
metode yang diperlukanIntoIterator
yang dapat menghasilkan jenis apa pun yang ditentukanIntoIterator::Item
. Biasanya, jika suatu tipe mengimplementasikanIntoIterator<Item=I>
, dengan konvensi ia juga memiliki dua metode ad-hoc:iter
daniter_mut
yang menghasilkan&I
dan&mut I
, masing-masing.Apa yang tersirat adalah bahwa kita dapat membuat fungsi yang menerima tipe yang memiliki
into_iter
metode (yaitu iterable) dengan menggunakan sifat terikat:Namun, kita tidak dapat * menggunakan suatu sifat yang terikat untuk meminta suatu tipe untuk memiliki
iter
metode atauiter_mut
metode, karena itu hanya konvensi. Kita dapat mengatakan bahwainto_iter
itu lebih banyak digunakan daripadaiter
atauiter_mut
.Alternatif untuk
iter
daniter_mut
Yang menarik untuk diamati adalah bahwa
iter
itu bukan satu-satunya cara untuk mendapatkan iterator yang menghasilkan&T
. Dengan konvensi (lagi), jenis koleksiSomeCollection<T>
distd
mana memilikiiter
metode juga memiliki referensi jenis berubah mereka&SomeCollection<T>
melaksanakanIntoIterator<Item=&T>
. Misalnya,&Vec<T>
mengimplementasikanIntoIterator<Item=&T>
, sehingga memungkinkan kita untuk beralih&Vec<T>
:Jika
v.iter()
setara dengan&v
kedua implementasi tersebutIntoIterator<Item=&T>
, mengapa Rust menyediakan keduanya? Ini untuk ergonomi. Dalamfor
loop, ini sedikit lebih ringkas untuk digunakan&v
daripadav.iter()
; tetapi dalam kasus lain,v.iter()
jauh lebih jelas daripada(&v).into_iter()
:Demikian pula, dalam
for
loop,v.iter_mut()
dapat diganti dengan&mut v
:Kapan harus menyediakan (mengimplementasikan)
into_iter
daniter
metode untuk suatu jenisJika tipe hanya memiliki satu "cara" untuk diulangi, kita harus mengimplementasikan keduanya. Namun, jika ada dua cara atau lebih yang dapat diulangi, kita harus menyediakan metode ad-hoc untuk setiap cara.
Misalnya,
String
tidak menyediakaninto_iter
atauiter
karena ada dua cara untuk mengulanginya: untuk mengulangi perwakilannya dalam byte atau untuk mengulangi perwakilannya dalam karakter. Alih-alih, ia menyediakan dua metode:bytes
untuk iterasi byte danchars
untuk iterasi karakter, sebagai alternatifiter
metode.* Yah, secara teknis kita bisa melakukannya dengan menciptakan suatu sifat. Tetapi kemudian kita perlu
impl
sifat itu untuk setiap jenis yang ingin kita gunakan. Sementara itu, banyak tipe yangstd
sudah diimplementasikanIntoIterator
.sumber