[英]What are the differences between my for loop variations in Rust?
我通过阅读“这本书”来自学 Rust。 当我通过此链接进行第一个练习时: https ://doc.rust-lang.org/book/ch08-03-hash-maps.html,我发现使用 for 循环获取正确的类型真的很棘手。 我最终通过反复试验得到了代码,但我对它的工作方式感到非常困惑。
在这里,我简化了我遇到的一些问题并将我的问题留在了内联。 我尝试了尽可能多的变化,但我认为主要的困惑是:
loop1
和loop2
的区别(我相信3和4、5和6是类似的)loop1
和loop2
中的内联问题loop3_bad
有什么问题?
fn main() {
// using mut because it is needed in the original code
let mut list = vec![10, 14, 10, 12, 9, -2, 14, 10, 14];
let ret1 = loop1(&list);
let ret2 = loop2(&list);
// let ret3 = loop3_bad(&mut list);
let ret4 = loop4(&mut list);
let ret5 = loop5(&mut list);
let ret6 = loop6(&mut list);
let ret7 = loop7(&mut list);
println!("loop1 ret={:?}", ret1);
println!("loop2 ret={:?}", ret2);
// println!("loop3 ret={:?}", ret3);
println!("loop4 ret={:?}", ret4);
println!("loop5 ret={:?}", ret5);
println!("loop6 ret={:?}", ret6);
println!("loop7 ret={:?}", ret7);
}
fn loop1(list: &Vec<i32>) -> Option<f64> {
if list.is_empty() {
None
} else {
let mut sum: f64 = 0.0;
for &i in list {
sum += f64::from(i);
// cannot write f64::from(*i)
// error would be:
// error[E0614]: type `i32` cannot be dereferenced
//
// How should I read the for syntax?
// Is it because "&i" of &i32 type, therefore i is of "i32" type?
// or should I treat "&i" a declaration that i is of "&i32" type?
}
Some(sum/list.len() as f64)
}
}
fn loop2(list: &Vec<i32>) -> Option<f64> {
if list.is_empty() {
None
} else {
let mut sum: f64 = 0.0;
for i in list {
sum += f64::from(*i);
// cannot write f64::from(i)
// error would be:
// the trait `From<&i32>` is not implemented for `f64`
//
// is i of "&i32" type?
// is the dereferencing required here?
}
Some(sum/list.len() as f64)
}
}
// This one causes compilation error, but why?
// If `list` is moved by the loop, why didn't this cause issue in loop1()?
//
// Rust ERROR:
//
// error[E0382]: borrow of moved value: `list`
// --> for_loop_example.rs:65:18
// |
// 57 | fn loop3(list: &mut Vec<i32>) -> Option<f64> {
// | ---- move occurs because `list` has type `&mut Vec<i32>`, which does not implement the `Copy` trait
// ...
// 62 | for &mut i in list {
// | ----
// | |
// | `list` moved due to this implicit call to `.into_iter()`
// | help: consider borrowing to avoid moving into the for loop: `&list`
// ...
// 65 | Some(sum/list.len() as f64)
// | ^^^^ value borrowed here after move
// |
// note: this function takes ownership of the receiver `self`, which moves `list`
//
// fn loop3_bad(list: &mut Vec<i32>) -> Option<f64> {
// if list.is_empty() {
// None
// } else {
// let mut sum: f64 = 0.0;
// for &mut i in list {
// sum += f64::from(i);
// }
// Some(sum/list.len() as f64)
// }
// }
fn loop4(list: &mut Vec<i32>) -> Option<f64> {
if list.is_empty() {
None
} else {
let mut sum: f64 = 0.0;
for i in &*list {
// what does &*list even do?
sum += f64::from(*i);
}
Some(sum/list.len() as f64)
}
}
fn loop5(list: &mut Vec<i32>) -> Option<f64> {
if list.is_empty() {
None
} else {
let mut sum: f64 = 0.0;
for &i in &*list { // similar to loop4, excpet for using &i
sum += f64::from(i);
}
Some(sum/list.len() as f64)
}
}
fn loop6(list: &mut Vec<i32>) -> Option<f64> {
if list.is_empty() {
None
} else {
let mut sum: f64 = 0.0;
for &mut i in &mut *list { // looking similar to loop5
sum += f64::from(i);
}
Some(sum/list.len() as f64)
}
}
fn loop7(list: &mut Vec<i32>) -> Option<f64> {
if list.is_empty() {
None
} else {
let mut sum: f64 = 0.0;
for i in &mut *list { // looking similar to loop4, except for using mut
sum += f64::from(*i);
}
Some(sum/list.len() as f64)
}
}
这里几乎没有构建块。
当您遍历&Vec
时,您将返回对其内容的共享引用。 也就是说,遍历&Vec<i32>
将为您提供&i32
类型的元素。 当您遍历&mut Vec
时,您会返回可变引用 - &mut i32
- 它允许您更改,而不仅仅是检查内容(同样,当您遍历Vec
时,您将拥有i32
,但这与我们的讨论无关)。
第二件重要的事情是,在 Rust 中,实际上有两种方法可以取消引用:第一种是通常的*
,第二种是使用模式。 当我绑定一个新变量时,
let x = value;
x
实际上不仅仅是一个名字,而是一个模式(一个无可辩驳的模式)。 我使用了标识符模式,它允许我将值绑定到一个名称,但也有其他类型。 其中之一是参考模式: &
或&mut
然后是嵌套模式。 它所做的是取消引用该值,然后将嵌套模式绑定到取消引用的值。 因此,以下内容:
let &i = &value;
相当于let i = value;
(您还可以看到对称性,这就是选择此语法的原因)。
不只是let
允许模式:Rust 中的任何绑定都可以。 参数、 match
参数以及for
循环。
有了这些知识,让我们一一了解循环:
在loop1
中,我们正在迭代&Vec<i32>
。 这给了我们&i32
s。 但是,我们使用&i
是模式——这意味着i
只绑定到i32
部分。 由于这不是引用,因此您不能再取消引用它。
loop2
是相同的,但没有参考模式。 这里i
是&i32
,所以你必须取消引用它。
loop3_bad
很有趣。 它与loop1
相同,但具有可变引用。 但它不起作用。 共享引用是Copy
,因此您可以访问list
即使它被循环移动 - 但可变引用不是,因此编译器会因“借用移动值”而出错。
其余的循环正在执行所谓的reborrow ,已经解释了共享/可变引用的各种组合以及使用/不使用参考模式。 重新借用只是&*
或&mut *
,这是取消引用然后立即获取引用。 听起来并不有趣,实际上它对共享引用没有影响,但是对于可变引用它有重要的语义:你不能复制一个可变引用,但你可以重新借用它。 &mut *v
使您可以访问可能更短的生命周期,而不会影响原始引用。 一旦不再需要重新借用的引用,您可以丢弃它并再次使用原始引用。
由于我们只重新借用,因此不会移动list
。 通常编译器会自动执行重借 - 请参阅可变引用是否具有移动语义? - 但for
循环,它不会。 这就是为什么loop3
没有重新借用就失败了,但是loop5
成功了
顺便说一句,不要将&Vec<i32>
作为参数,而是使用&[i32]
: 为什么不鼓励接受对字符串 (&String)、Vec (&Vec) 或框 (&Box) 的引用作为函数参数?
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.