Rust中使用变量的途径有三种,所有权(ownership), 不可变引用(immutable reference) 和可变引用(mutable reference)。如果学习了Rust,我们会发现这三种使用值的办法充斥着Rust的角角落落。在此,我们来研究一下Rust Iterator,及其与这三种方式的联系。
首先,所有的迭代器都实现了 Iterator Trait
,而 next
方法无疑是 Iterator Trait
中最基本的方法。我们来看下它的方法签名:
pub trait Iterator {
type Item;
fn next(&mut self) -> Option<Self::Item>;
}
我们可以看到,next
方法的参数是 &mut self
,也就是 可变的迭代器引用
。因此,call next
方法的类型一定是一个可变的迭代器,不可变迭代器不能call next
方法。
next
方法的返回值是 Option<Self::Item>
这个 enum
,目的是可以让我们判断是否有 next value
。如果有下一个值,那么返回值就是 Some<Item Type>
,如果没有下一个值,那么返回值就是 None
。我们可以通过 match
来判断。
现在我们来看 Vec<T>
生成迭代器的办法。有三种方式可以实现这个目的,分别是 iter
,iter_mut
和 into_iter
。根据我的理解,这三种方法分别需要 Vec<T>
的 &self
, &mut self
和 self
,即向量的不可变引用,可变引用和向量的所有权。当然了,需要所有权的方法,会 move
原向量的所有权,所以原向量不再能够使用。
这三个方法生成的迭代器有什么区别呢?根据我的理解,就是在实现 Iterator Trait
的时候,这三种迭代器的 associated type
,即关联类型不一样。这就会导致调用 next
方法的时候:
iter
返回的是值的不可变引用,即&T
iter_mut
返回的是可变引用,即&mut T
into_iter
返回的是T
类型的值
让我们来写代码验证我们的猜想。首先来看 into_iter
:
let v = vec![String::from("good")];
for mut i in v.into_iter() {
i.push_str(" morning");
println!("{}",i);
}
println!("{:?}",v);
123456
3 | for mut i in v.into_iter() {
| - value moved here
...
7 | println!("{:?}",v);
| ^ value borrowed here after move
1234567
通过报错信息我们可以看到,v
的所有权已经被 move
了,不再拥有向量的所有权。如果我们注释掉最后一句,则可以正常使用。因为我们用 mut i
来代表 mut String
,所以我们可以改变 String
的值。
let v = vec![String::from("good")];
for mut i in v.into_iter() {
i.push_str(" morning");
println!("{}",i);
}
12345
good morning
1
let v = vec![1];
for i in v.iter() {
assert_eq!(i, &1);
}
1234
通过 assert_eq!
这个宏,我们可以验证,i
就是 &1
。
let v = vec![1];
for i in v.into_iter() {
assert_eq!(i, 1);
}
1234
而通过 assert_eq!
这个宏,我们可以验证,此时 i
是 1
。
let mut v = vec![1];
for i in v.iter_mut() {
*i = 555;
}
println!("{:?}",v);
12345
这段代码的运行结果是:
[555]
1
实际上,根据我们的分析,这里的 i
的类型是 &mut i32
,所以解引用之后可以改变其值。同时,iter_mut
只需要 &mut self
而不是所有权,所以后面还可以正常使用 v
。