for-loop - Rust 中的 for 循环变体之间有什么区别?

标签 for-loop rust compiler-errors

我正在通过阅读“这本书”来自学 Rust。当我通过此链接进行第一个练习时:https://doc.rust-lang.org/book/ch08-03-hash-maps.html ,我发现使用 for 循环获取正确的类型确实很棘手。我最终通过反复试验使代码可以工作,但我对它的工作原理感到非常困惑。

在这里,我简化了我遇到的一些问题,并将我的问题留在了内文中。我尝试了尽可能多的变化,但我认为主要的困惑是:

  • loop1loop2 之间的区别(我相信 3 和 4、5 和 6 是类似的)
  • loop1loop2 中的内联问题
  • 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)
        }
    }

最佳答案

这里几乎没有构建 block 。

当您迭代 &Vec 时,您将获得对其内容的共享引用。也就是说,迭代 &Vec<i32>会给你 &i32 类型的元素。当您迭代&mut Vec时,你会得到可变引用 - &mut i32 - 允许您更改内容,而不仅仅是检查内容(此外,当您迭代 Vec 时,您将拥有 i32 ,但这与我们的讨论无关)。

要知道的第二件事是,在 Rust 中,实际上有两种方法来取消引用引用:第一种是通常的 * ,第二是使用模式。当我绑定(bind)一个新变量时,

let x = value;

x其实不只是一个名字,而是一个pattern (一个irrefutable一个)。我用过identifier pattern这允许我将值绑定(bind)到名称,但还有其他类型。其中之一是the reference pattern :&&mut然后是嵌套模式。它的作用是取消引用该值,然后将嵌套模式绑定(bind)到取消引用的值。因此,如下:

let &i = &value;

相当于let i = value; (您还可以看到对称性,这就是选择此语法的原因)。

不仅仅是let允许模式:Rust 中的任何绑定(bind)都允许。参数,match参数,以及 for循环。

有了这些知识,让我们一一理解循环:

loop1 ,我们正在迭代 &Vec<i32> 。这给了我们 &i32 s。但是,我们正在使用 &i是模式 - 这意味着 i仅绑定(bind)到i32部分。由于这不是引用,因此您不能再取消引用它。

loop2是相同的,但没有引用模式。这里i&i32 ,因此您必须取消引用它。

loop3_bad很有趣。它的作用与loop1相同。但具有可变引用。但这不起作用。共享引用文献是Copy ,这样您就可以访问list即使在它被循环移动之后 - 但可变引用则不然,因此编译器会犯“借用移动值”的错误。

其余的循环正在执行所谓的“重新借用”,已经解释了共享/可变引用以及使用/不使用引用模式的各种组合。重新借用只是&*&mut * ,这就是解除引用然后立即获取引用。听起来不太有趣,而且它确实对共享引用没有影响,但对于可变引用它有重要的语义:你不能复制可变引用,但你可以重新借用它。 &mut *v使您能够获得可能较短的生命周期,而不会影响原始引用。一旦不再需要重新借用的引用,您可以将其丢弃并再次使用原始引用。

由于我们只重借,list不为所动。通常编译器会自动执行重新借用 - 请参阅 Do mutable references have move semantics? - 但与 for循环,但它没有。这就是为什么loop3没有重新借用就失败了,但是 loop5成功

顺便说一句,不要拿&Vec<i32>作为参数,使用 &[i32]相反:Why is it discouraged to accept a reference to a String (&String), Vec (&Vec), or Box (&Box) as a function argument?

关于for-loop - Rust 中的 for 循环变体之间有什么区别?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72808249/

相关文章:

python - 并行 for 循环 python

java - TwoSums 代码仅返回 [0,0]

types - 如何表示缺失或无效的值?

sdk - CUDA 5.0 : checkCudaErrors fails to find correct “check” method if class has a “check” method

c# - 错误 1001,找不到实际存在的 .manifest c# visual studio 2010

javascript - For循环生成多维数组

multithreading - for循环中的异步请求返回

rust - 尽管返回了所需的值,但为什么我的代码返回 '()'?

Rust 字符串生命周期和迭代器适配器(生命周期编译错误)

java - “找不到符号”或“无法解析符号”错误是什么意思?