【发布时间】:2019-10-11 17:05:58
【问题描述】:
在 Python 中,如果我有一个列表并想为每个元素添加 10,我会这样做:
bar = [2,4,5,6,7]
bar = [x + 10 for x in bar]
导致:[12,14,15,16,17]。如何在 Rust 中做到这一点?是做一个 for 循环并遍历每个向量元素的唯一方法吗?
【问题讨论】:
在 Python 中,如果我有一个列表并想为每个元素添加 10,我会这样做:
bar = [2,4,5,6,7]
bar = [x + 10 for x in bar]
导致:[12,14,15,16,17]。如何在 Rust 中做到这一点?是做一个 for 循环并遍历每个向量元素的唯一方法吗?
【问题讨论】:
Rust 的实现方式与 Python 非常相似:使用迭代器! Python 的列表推导大致等价于 iter::map 获取新元素,iter::collect 收集到新向量(或某种其他类型的集合)中。
例如,如果bar 是Vec<i32>(或任何其他原始整数类型),并且您想为每个元素添加 10,请尝试
bar = bar.into_iter().map(|x| x + 10).collect();
或者,您可以使用
就地改变元素bar.iter_mut().for_each(|x| *x += 10);
这基本上就像一个 for 循环,但更简洁一些。这通常比第一种方法更有效,因为您不需要分配新向量(足够智能的编译器可能能够避免这种情况)。唯一的缺点是这个版本不太灵活。输出仍然需要是一个向量;你不能切换到哈希集或你有什么。您也无法保留旧向量的副本。请参阅下面的一些可能的示例。
fn main() {
let mut bar = vec![2, 4, 5, 6, 7];
// Overwrite the old vector
bar = bar.into_iter().map(|x| x + 10).collect();
println!("new bar: {:?}", bar);
let bar = vec![2, 4, 5, 6, 7];
// Make a completely new vector
// Note that this works only because i32 implements the Copy trait,
// so we can make copies of the elements of bar without any problems
// In more general situations, we may need to clone each element
let foo: Vec<_> = bar.iter().map(|&x| x + 10).collect();
println!("old bar: {:?} (it's still around)", bar);
println!("new foo: {:?}", foo);
use std::collections::HashSet;
let bar = vec![2, 4, 5, 6, 7];
// transform the data and collect it into a HashSet
// instead of a vector
let bar: HashSet<_> = bar.into_iter().map(|x| x + 10).collect();
println!("new bar: {:?} (note that now bar is unordered)", bar);
let mut bar = vec![2, 4, 5, 6, 7];
// Overwrite the old vector in place
bar.iter_mut().for_each(|x| *x += 10);
println!("new bar: {:?}", bar);
}
【讨论】:
strings 是Vec<String> 类型的向量,那么您将无法执行strings.for_each().map(|s| *s = s.len()),因为这会改变每个字符串的类型以及整个向量。
这是基本代码,说明了如何按照问题默认的方式进行操作。它可能对像我这样的 Rust 初学者有用:
fn increment_mut(p: &mut Vec<i32>, to_add: i32){
for i in 0..p.len() {
p[i] += to_add;
}
}
fn main() {
let mut p = vec![2, 4, 5, 6, 7];
increment_mut(&mut p, 10);
// Print the complete vector in Debug.
println!("{:?}", p)
}
$ cargo run
[12, 14, 15, 16, 17]
iter_mut
fn increment_mut2(p: &mut Vec<i32>, to_add: i32) {
for x in p.iter_mut() {
*x += to_add;
}
}
fn main() {
let mut p = vec![2, 4, 5, 6, 7];
increment_mut2(&mut p, 10);
// Print the complete vector in Debug.
println!("{:?}", p)
}
$ cargo run
[12, 14, 15, 16, 17]
【讨论】:
&mut Vec<_> 是非惯用语 (Why is it discouraged to accept a reference to a String (&String), Vec (&Vec), or Box (&Box) as a function argument?)。有自修改运算符 (+=)。 p.iter() 通常写成&。不需要在打印 for 循环中取消引用 (Does println! borrow or own the variable?)。