【问题标题】:What is the purpose of `&` before the loop variable?循环变量之前的`&`的目的是什么?
【发布时间】:2019-12-11 19:58:53
【问题描述】:

代码&i in list&的作用是什么?如果我删除&,它会在largest = i 中产生错误,因为它们的类型不匹配(其中i&32ii32)。但是&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`

【问题讨论】:

标签: reference rust pattern-matching destructuring ampersand


【解决方案1】:

所以通常当你使用for i in list 时,循环变量i 的类型是&i32

但是当您改为使用for &i in list 时,您并没有取消引用任何东西,而是您正在使用模式匹配来显式地解构引用 这将使i 成为i32 类型。

请参阅 Rust 文档,了解我们在此处使用的 for-loop 循环变量 being a patternreference pattern。另请参阅 destructuring pointers 上的 Rust By Example 一章。

 

解决此问题的另一种方法是保持 i 原样,然后将 i 与对 largest 的引用进行比较,然后在分配给 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(即i32)分配给对i32 的引用的引用。这与循环变量情况下的模式匹配和解构完全无关。

【讨论】:

    【解决方案2】:

    这是解构的效果。我不会在这里完全描述该功能,但简而言之:

    在许多语法上下文中(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 但是!那是因为“引用已被模式解构/删除”。

    一个常见的误解是,模式中的语法与表达式中的相同语法具有完全相反的效果! 如果您有一个 a 类型为 [T; 1] 的变量,那么表达式[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`
    

    这也解释了你的问题:

    它似乎以某种方式取消引用,但为什么在下面的代码中,它不起作用?

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

    因为你在表达式端使用了&,它添加了一层引用。


    最后是关于你的循环:当迭代一个切片时(就像你在这里所做的那样),迭代器产生对切片元素的引用。所以在for i in list {} 的情况下,i 的类型为&i32。但是分配largest = i; 需要在右侧有一个i32。您可以通过两种方式实现此目的:通过解引用运算符 *(即 largest = *i;)解引用 i 或解构循环模式中的引用(即 for &i in list {})。


    相关问题

    【讨论】:

    • 感谢您如此明确的回答。我认为将解构视为代数“取消”操作可能会有所帮助。例如,在let &c = &foo - 如果您“取消”&,则c 将设置为foo。 “c 的引用设置为 foo 的引用”,因此 c 设置为 foo
    • 感谢您的解释。这个例子非常有帮助,我认为更好
    猜你喜欢
    • 1970-01-01
    • 2011-10-04
    • 2011-03-14
    • 1970-01-01
    • 2013-08-03
    • 1970-01-01
    • 2010-09-29
    相关资源
    最近更新 更多