Rust中for循环的确切定义是什么?

Avi*_*jit 5 iterator for-loop reference rust

我来自C(在较小程度上,C++)背景.我写了以下代码片段:

fn main() {
    let my_array = [1, 2, 3];
    let print_me = |j| println!("= {}", j);
    for k in my_array.iter() {
        print_me(k);
    }
}
Run Code Online (Sandbox Code Playgroud)

这编译并按预期运行,但后来我指定了传递给闭包的参数的类型print_me:

fn main() {
    let my_array = [1, 2, 3];
    let print_me = |j: i32| println!("= {}", j);
    for k in my_array.iter() {
        print_me(k);
    }
}
Run Code Online (Sandbox Code Playgroud)

我收到了编译错误:

error[E0308]: mismatched types
 --> src/main.rs:6:22
  |
6 |             print_me(k);
  |                      ^
  |                      |
  |                      expected i32, found &{integer}
  |                      help: consider dereferencing the borrow: `*k`
  |
  = note: expected type `i32`
             found type `&{integer}`
Run Code Online (Sandbox Code Playgroud)

现在,这个困惑我,直到我换k&kfor语句,它工作得很好:

fn main() {
    let my_array = [1, 2, 3];
    let print_me = |j: i32| println!("= {}", j);
    for &k in my_array.iter() {
        print_me(k);
    }
}
Run Code Online (Sandbox Code Playgroud)

似乎我误解了for语法本身 - 或者可能是迭代器的确切工作方式 - 或者可能是指针的使用语法与指针相关[在C++中相关但不同].

在结构for A in B { C1; C2; ... Cn },究竟是什么AB应该是什么?

DK.*_*DK. 11

首先,这里是参考文献中定义的for链接.

总而言之,B是任何表达式,它可以转换为可以转换为实现Iterator<T>特征的值的东西,同时A是一个绑定类型值的无可辩驳的模式T.

在特定情况下,slice::iter返回一个Iter<i32>,它实现了Iterator<Item = &i32>.也就是说,它不会产生i32s,它会产生&i32s.

因此,在第一和第二示例中,k实际上是绑定到&i32s而不是i32s.当您指定闭包的类型时,实际上是指定了错误的类型.最后一个例子工作的原因是因为A是一个模式,而不是一个变量名.什么&k真正做的是"去结构化"的&i32,结合i32部分的命名变量k.

"无可辩驳的"部分仅仅意味着模式必须始终有效.例如,你不能这样做for Some(x) in thingy,其中thingy工具Iterator<Option<_>>; Some(x)对于迭代器中的每个元素都不一定有效; 因此,它是一种可反复的模式.


Mat*_* M. 6

许多迭代器实际上返回一个引用而不是一个值.可以肯定的是,你必须检查返回类型.iter(),它应该是以下形式Iterator<Item = X>:X将是返回的变量的类型.

所以在这里:

fn main() {
    let my_array = [1, 2, 3];
    let print_me = |j: i32| println!("= {}", j);
    for k in my_array.iter() {
        print_me(k);
    }
}
Run Code Online (Sandbox Code Playgroud)

X&i32(引用i32),因此k具有类型&i32.

这就是为什么在调用print_me时会出现错误:&i32传递到i32期望的位置.


这里有多种可能的修复方法:

  1. 指定不同的类型print_me:

    let print_me = |j: &i32| println!("= {}", j);
    
    Run Code Online (Sandbox Code Playgroud)
  2. 取消引用的价值k:

    print_me(*k);
    
    Run Code Online (Sandbox Code Playgroud)
  3. k通过循环中的解构来改变类型:

    for &k in my_array.iter() { ... }
    
    Run Code Online (Sandbox Code Playgroud)

进行解构是因为for .. in接受了一个无可辩驳的模式,所以你可以像在match表达式中那样进行模式匹配,除了变量的类型必须匹配(否则你得到编译器时间错误).

为了更好地说明它,我们可以使用一个稍微复杂的例子:

fn main() {
    let my_array = [(1, 2), (2, 3), (3, 4)];
    let print_me = |a: i32, b: i32| println!("= {} {}", a, b);
    for &(j, k) in my_array.iter() {
        print_me(j, k)
    }
}
Run Code Online (Sandbox Code Playgroud)

类型my_array[(i32, i32)]:2的元组数组i32.的结果.iter()因此是类型的Iterator<Item = &(i32, i32)>:一个迭代到2元组的参考i32又名&(i32, i32).

当我们使用无可辩驳的模式时&(j, k),发生的是我们对元组进行解构,以便:

  • 第一个元素绑定j(推断为类型i32,只有因为i32Copy)
  • 第二个元素绑定到k((推断为类型i32)

jk因此成为的临时副本i32内这个元素.