当涉及到集合中元素的所有权时,迭代器起着极其重要的作用。
在下面这些例子中,我们将使用Vec<String>,故意使用String作为元素(它没有实现Copy trait:String默认是值传递,不是引用传递,也不是值复制),这样我们就可以在向量中演示其移动语义。
让我们从一个对names进行迭代的for-loop开始。为什么是for-loop?我们后面将讨论这个问题。
fn main() { let names = vec![ String::from("John"), String::from("Jane"), ]; for name in names { println!("{}", name); } }
|
但是,当直到我们在for-loop下面添加一个print语句......在那里编译器开始抱怨。
fn main() { let names = vec![ String::from("John"), String::from("Jane"), ]; for name in names { println!("{}", name); } println!("Names: {:?}", names); }
|
编译器:
error[E0382]: borrow of moved value: `names` --> src/main.rs:11:29
|
显然,在循环之前有一个 "隐含的.into_iter调用"。
等等,隐式?为什么呢!?但是,好吧,让我们添加那个隐式的.into_iter。
在加入into_iter()之后,我们预计程序仍然不能编译。
fn main() { let names = vec![ String::from("John"), String::from("Jane"), ];
for name in names.into_iter() { println!("{}", name); }
println!("Names: {:?}", names); }
|
error[E0382]: borrow of moved value: `names` --> src/main.rs:11:29
|
嗯,让我们给names.into_iter()分配一个变量。这样我们就可以直观地看到这个动作。请注意,我们仍然希望程序不被编译。
fn main() { let names = vec![ String::from("John"), String::from("Jane"), ]; let names_iter = names.into_iter(); for name in names_iter { println!("{}", name); } println!("Names: {:?}", names); }
|
error[E0382]: borrow of moved value: `names` --> src/main.rs:11:29
|
发生的情况是,使用 into_iter,我们正在将元素从 names 移到 names_iter。结果是,我们不能再使用names了!
我们该怎么做呢?
我们可以使用.iter()从名字中借用元素:
fn main() { let names = vec![ String::from("John"), String::from("Jane"), ];
for name in names.iter() { println!("{}", name); }
println!("Names: {:?}", names); }
|
正常输出:
John Jane Names: ["John", "Jane"]
|
我们可以使用一个slice(实现了IntoIterator):
fn main() { let names = vec![ String::from("John"), String::from("Jane"), ];
for name in &names { println!("{}", name); }
println!("Names: {:?}", names); }
|
正常输出如果你的程序允许,我们可以把代码行调换一下,先借用名字,然后再移动它。
fn main() { let names = vec![ String::from("John"), String::from("Jane"), ];
println!("Names: {:?}", names);
for name in names { println!("{}", name); } }
|
我们也可以克隆该vector。请注意,克隆是有成本的。
fn main() { let names = vec![ String::from("John"), String::from("Jane"), ];
for name in names.clone() { println!("{}", name); }
println!("Names: {:?}", names); }
|
函数式编程
使用函数式编程成语使得当你想对元素进行迭代时,迭代器的作用更加明显(尤其是当你来自于不用直接处理迭代器的编程语言时)。
与for-loop不同,没有隐含的.into_iter()调用。因此,对于向量,我们总是需要调用.iter()、.into_iter()等来获得一个迭代器。
下面代码不能编译,因为我们需要对元素进行迭代,这意味着我们需要使用例如.iter()获得一个迭代器。
fn main() { let names = vec![ String::from("John"), String::from("Jane"), ]; names.for_each(|name| println!("{}", name)); println!("{:?}", names); }
|
error[E0599]: `Vec<String>` is not an iterator --> src/main.rs:7:11
|
这里,我们使用了.iter()和.for_each()的组合。
迭代器返回一个元素的引用。我们仍然可以在之后使用names对象。
fn main() { let names = vec![ String::from("John"), String::from("Jane"), ]; names .iter() .for_each(|name| println!("{}", name)); println!("{:?}", names); }
|
输出:
如果你想移动这些元素,可以使用.into_iter()。然而,请注意,之后我们就不能使用这个向量了。
fn main() { let names = vec![ String::from("John"), String::from("Jane"), ]; names .into_iter() .for_each(|name| println!("{}", name)); println!("{:?}", names); }
|
error[E0382]: borrow of moved value: `names` --> src/main.rs:12:22
|
你需要重新设计你的程序,使你不使用被移动的元素。这里,我们把打印语句移到了前面。
fn main() { let names = vec![ String::from("John"), String::from("Jane"), ]; println!("{:?}", names);
names .into_iter() .for_each(|name| println!("{}", name)); }
|
我们也可以在调用.into_iter()之前克隆该向量。
fn main() { let names = vec![ String::from("John"), String::from("Jane"), ];
names.clone() .into_iter() .for_each(|name| println!("{}", name)); println!("{:?}", names); }
|