在循环变量之前加上`&`的目的是什么?

20
在代码&i in list中,&的目的是什么?如果我去掉&,那么largest = i就会产生错误,因为它们具有不匹配的类型(其中i&32list中的ii32)。但是,&i如何将i转换为i32
fn largest(list: &[i32]) -> i32 {
    println!("{:?}", list);
    let mut largest = list[0];
    for &i in list {
        if i > largest {
            largest = i;
        }
    }
    largest
}

fn main() {
    let hey = vec![1, 3, 2, 6, 90, 67, 788, 12, 34, 54, 32];
    println!("The largest number is: {}", largest(&hey));
}

Playground

代码似乎在某种程度上进行了取值,但在下面的代码中,为什么它没有起作用呢?

fn main() {
    let mut hey: i32 = 32;
    let x: i32 = 2;
    hey = &&x;
}

它说:

4 |     hey = &&x;
  |           ^^^ expected i32, found &&i32
  |
  = note: expected type `i32`
             found type `&&i32`

2
与您的问题无关,但该函数可以重写为 list.iter().copied().max().expect("empty list")。这可能对您有用。 - Lukas Kalbertodt
2个回答

26

通常情况下,当使用 for i in list 时,循环变量 i 的类型将为 &i32

但是,当你使用 for &i in list 时,你并没有对任何内容进行解引用,而是使用模式匹配来明确地解构引用,这将使得 i 变成类型为 i32

请查看 Rust 文档关于 for 循环变量作为模式的说明以及我们在此处使用的引用模式。还可以参考 Rust By Example 章节中有关解构指针的内容。

 

另一种解决方法是保持 i 不变,然后将 ilargest 的引用进行比较,并在分配给 largest 之前对 i 进行解引用:

fn largest(list: &[i32]) -> i32 {
    println!("{:?}", list);
    let mut largest = list[0];
    for i in list {
        if i > &largest {
            largest = *i;
        }
    }
    largest
}

 


fn main() {
    let mut hey: i32 = 32;
    let x: i32 = 2;
    hey = &&x;
}
这样做是不起作用的,因为你正在将一个类型的变量hey赋值给指向类型的引用的引用。这与循环变量案例中的模式匹配和解构无关。

24
这是 解构 的效果。我不会在这里完全描述该功能,但简而言之:
在许多语法上下文中(let 绑定、for 循环、函数参数等),Rust 需要一个“模式”。这个模式可以是一个简单的变量名,但它也可以包含一些“解构元素”,比如 &。然后 Rust 将一个值绑定到这个模式上。一个简单的例子可能是这样的:
let (a, b) = ('x', true);

在右侧有一个类型为(char, bool)(元组)的值。该值绑定到左侧模式((a, b))。由于模式中已经定义了“结构”(具体来说是元组),因此该结构被移除,ab绑定到元组的元素。因此,a的类型是charb的类型是bool
这适用于一些结构,包括数组:
let [x] = [true];

再次强调,右侧的值是一个类型为[bool; 1](数组)的值,而左侧则是一个数组形式的模式。单个数组元素被绑定到x,这意味着x的类型是bool不是[bool; 1]!毫不奇怪的是,这也适用于引用!
let foo = 0u32;
let r = &foo;
let &c = &foo;

在这里,foo的类型为u32,因此,表达式&foo的类型为&u32r的类型也是&u32,因为它是一个简单的let绑定。然而,c的类型是u32!这是因为模式“引用已被解构/删除”。 一个常见的误解是,模式中的语法与相同语法在表达式中具有完全相反的效果! 如果您有一个类型为[T; 1]的变量a,那么表达式[a]的类型为[[T; 1]; 1] → 它添加了一些东西。然而,如果将a绑定到模式[c],那么y的类型为T → 它删除了一些东西。
let a = [true];    // type of `a`: `[bool; 1]`
let b = [a];       // type of `b`: `[[bool; 1]; 1]`
let [c] = a;       // type of `c`: `bool`

这也解释了你的问题:

It seems like it is somehow dereferencing, but then why in the below code, it is not working?

fn main() {
   let mut hey:i32 = 32;
   let x:i32 = 2;
   hey = &&x;
}
因为您在表达式侧使用了&,它会添加一层引用。
所以最终关于您的循环:当遍历一个切片时(就像在这里所做的那样),迭代器会产生对切片元素的引用。因此,在for i in list {}的情况下,i的类型是&i32。但largest = i;需要右侧为i32。你可以通过两种方法实现这个目标:要么通过解引用运算符*来解引用i(即largest = *i;),要么在循环模式中解构引用(即for &i in list {})。
相关问题

谢谢您提供如此清晰的答案。我认为将解构视为代数“消除”操作可能会有所帮助。例如,在 let &c = &foo 中 - 如果您“取消” &,则 c 将设置为 foo。“c 的引用被设置为 foo 的引用”,因此 c 被设置为 foo - navkast
感谢您的解释。这个例子非常有帮助,我认为这是更好的答案。 - Regyn

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接