繁体   English   中英

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

[英]What are the differences between my for loop variations in Rust?

我通过阅读“这本书”来自学 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)
        }
    }

这里几乎没有构建块。

当您遍历&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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM