Tôi nghĩ rằng có một cái gì đó để làm rõ hơn một chút. Các loại bộ sưu tập, chẳng hạn như Vec<T>
và VecDeque<T>
, có into_iter
phương pháp mang lại T
vì chúng thực hiện IntoIterator<Item=T>
. Không có gì ngăn cản chúng tôi tạo ra một loại Foo<T>
nếu được lặp đi lặp lại, nó sẽ mang lại không T
chỉ một loại khác U
. Đó là, Foo<T>
thực hiện IntoIterator<Item=U>
.
Trong thực tế, có một số ví dụ trong std
: &Path
thực hiện IntoIterator<Item=&OsStr>
và &UnixListener
thực hiện IntoIterator<Item=Result<UnixStream>>
.
Sự khác biệt giữa into_iter
vàiter
Quay lại câu hỏi ban đầu về sự khác biệt giữa into_iter
và iter
. Tương tự như những người khác đã chỉ ra, sự khác biệt into_iter
là một phương pháp bắt buộc IntoIterator
có thể mang lại bất kỳ loại nào được chỉ định trong IntoIterator::Item
. Thông thường, nếu một loại thực hiện IntoIterator<Item=I>
, theo quy ước, nó cũng có hai phương thức đặc biệt: iter
và iter_mut
mang lại &I
và &mut I
tương ứng.
Điều nó ngụ ý là chúng ta có thể tạo một hàm nhận một kiểu có into_iter
phương thức (tức là nó có thể lặp lại) bằng cách sử dụng một đặc điểm bị ràng buộc:
fn process_iterable<I: IntoIterator>(iterable: I) {
for item in iterable {
// ...
}
}
Tuy nhiên, chúng tôi không thể * sử dụng một đặc điểm bị ràng buộc để yêu cầu một loại phải có iter
phương pháp hoặc iter_mut
phương pháp, bởi vì chúng chỉ là quy ước. Chúng ta có thể nói rằng nó into_iter
được sử dụng rộng rãi hơn iter
hoặc iter_mut
.
Các lựa chọn thay thế cho iter
vàiter_mut
Một điều thú vị khác để quan sát là đó iter
không phải là cách duy nhất để có được một trình vòng lặp mang lại &T
. Theo quy ước (một lần nữa), các kiểu tập hợp SomeCollection<T>
trong std
đó có iter
phương thức cũng có các kiểu tham chiếu bất biến của chúng &SomeCollection<T>
thực hiện IntoIterator<Item=&T>
. Ví dụ: &Vec<T>
triển khai IntoIterator<Item=&T>
, vì vậy nó cho phép chúng tôi lặp lại &Vec<T>
:
let v = vec![1, 2];
// Below is equivalent to: `for item in v.iter() {`
for item in &v {
println!("{}", item);
}
Nếu v.iter()
tương đương với &v
cả hai thực hiện IntoIterator<Item=&T>
, tại sao Rust lại cung cấp cả hai? Đó là cho công thái học. Trong for
các vòng lặp, nó ngắn gọn hơn một chút để sử dụng &v
hơn v.iter()
; nhưng trong các trường hợp khác, v.iter()
rõ ràng hơn nhiều so với (&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();
Tương tự, trong for
các vòng lặp, v.iter_mut()
có thể được thay thế bằng &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;
}
Khi nào cần cung cấp (hiện thực) into_iter
và iter
phương thức cho một loại
Nếu kiểu này chỉ có một cách thức mà thôi, thì chúng ta nên thực hiện cả hai. Tuy nhiên, nếu có hai cách trở lên có thể lặp đi lặp lại, thay vào đó chúng ta nên cung cấp một phương pháp đặc biệt cho mỗi cách.
Ví dụ, String
cung cấp không into_iter
phải iter
bởi vì có hai cách để lặp lại nó: lặp lại biểu diễn của nó theo byte hoặc lặp lại biểu diễn của nó trong các ký tự. Thay vào đó, nó cung cấp hai phương thức: bytes
để lặp các byte và chars
lặp lại các ký tự, như là các iter
phương thức thay thế cho phương thức.
* Vâng, về mặt kỹ thuật chúng ta có thể làm điều đó bằng cách tạo ra một đặc điểm. Nhưng sau đó chúng ta cần impl
đặc điểm đó cho từng loại chúng ta muốn sử dụng. Trong khi đó, nhiều loại trong std
đã thực hiện IntoIterator
.