`for...in` 循环中的 Rust 借用规则

Tia*_*Shi 5 rust borrowing

为什么这三个print_max功能都会起作用?哪一种是最佳实践?是 的for number in number_list快捷方式吗for number in number_list.iter()

fn main() {
    let number_list = vec![34, 50, 25, 100, 65];

    print_max_1(&number_list);
    print_max_2(&number_list);
    print_max_3(&number_list);
}

fn print_max_1(number_list: &[u16]) {
    let mut largest = &number_list[0]; // borrow the first number
    for number in number_list.iter() { // borrowing?
        if number > largest {
            largest = number;
        }
    }
    println!("The largest number is {}", largest);
}

fn print_max_2(number_list: &[u16]) {
    let mut largest = &number_list[0]; // borrow the first number
    for number in number_list {        // shortcut for .iter()?
        if number > largest {
            largest = number;
        }
    }
    println!("The largest number is {}", largest);
} 

fn print_max_3(number_list: &[u16]) {
    let mut largest = number_list[0];    // copying?
    for &number in number_list.iter() {  // borrowing?
        if number > largest {            // no error
            largest = number;
        }
    }
    println!("The largest number is {}", largest);
}
Run Code Online (Sandbox Code Playgroud)

为什么这不起作用?

fn print_max_4(number_list: &[u16]) {
    let mut largest = &number_list[0];
    for &number in number_list {
        if number > largest {
            largest = number;
        }
    }
    println!("The largest number is {}", largest);
} 
Run Code Online (Sandbox Code Playgroud)

错误消息显示 that largestis &u16while numberis u16。为什么不是number &u16

Bri*_*ian 5

让我们一一解决这些问题。

print_max_1

这里,largest是一个可变变量,它保存对 a 的不可变引用u16(即它保存 a &u16)。在循环中,number也是一个&u16, 其中largest, 是从 借用的number_list。和numberlarger被隐式取消引用以执行比较。如果 引用的值number大于 引用的值,则将包含在中larger的不可变引用的副本存储到中。numberlargest

print_max_2

在这种情况下,由于number_list本身是借用的,因此 的分析print_max_2与 相同print_max_1

print_max_3

这里largest有一个u16。你说得对,number_list[0]是复制的,但值得注意的是,这个复制品很便宜。在循环内, 的每个元素都number_list被复制并直接存储在 中number。如果number大于largest,则将新的最大值直接存储在 中largest。这是您编写的三个实现中最优化的一个,因为您消除了引用(即指针)引入的所有不必要的间接。

print_max_4

您再次存储对number_listin的第一个元素的引用largest,即largesta &u16。类似地,与 中的情况一样print_max_3number是 a u16,它将保存 中元素的副本number_list。然而,正如您所指出的,这个函数是有问题的孩子。

在循环内,编译器会指出两个错误:

  1. 您尝试比较两个没有PartialOrder定义的不同类型,即largest哪个是 a&u16number哪个是 a u16。Rust 不会尝试猜测这种比较的含义,因此为了解决这个问题,您必须使两者具有number相同largest的类型。在这种情况下,您想要做的是largest使用运算符显式取消引用,这将允许您将it 指向的内容与中包含的内容*进行比较。最终的比较看起来像 u16u16number
    if number > *largest { ... }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 您尝试将 a 存储u16在类型 的变量中&u16,这是没有意义的。不幸的是,在这里你会碰壁。在循环中,您拥有的只是从 复制的数字的值number_list,但largest需要保存对 a 的引用u16。我们不能简单地number在这里借用(例如通过编写largest = &number),因为numberwill 在循环结束时被删除(即超出范围)。唯一的解决方法是print_max_2通过存储最大值本身而不是指向它的指针来恢复到。

至于是否for number in number_list是捷径for number in number_list.iter(),答案是否定。前者将取得 的所有权number_list,并且在每次迭代期间,number取得 中下一个值的所有权number_list。相比之下,后者仅执行借位,并且在循环的每次迭代期间,number接收对 的下一个元素的不可变引用number_list

在这种特定情况下,这两个操作看起来是相同的,因为获得不可变引用的所有权只需要制作一个副本,从而使原始所有者完好无损。有关更多信息,请参阅有关和 之间差异的相关问题的答案.into_iter().iter()