【问题标题】:What is the exact definition of the for loop in Rust?Rust 中 for 循环的确切定义是什么?
【发布时间】:2015-01-29 06:05:36
【问题描述】:

我来自 C(在较小程度上是 C++)背景。我写了如下代码sn-p:

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

这按预期编译并运行,但随后我指定了传递给闭包 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);
    }
}

我得到一个编译错误:

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}`

现在这让我很困惑,直到我在 for 语句中将 k 更改为 &k,效果很好:

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

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

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

【问题讨论】:

    标签: for-loop reference iterator rust


    【解决方案1】:

    首先,这是the definition of for in the reference的链接。

    总而言之,B 是任何表达式,其计算结果可以转换为实现 Iterator<T> 特征的值,而 A 是一个无可辩驳的模式,它绑定了输入T

    在您的特定情况下,slice::iter 返回一个Iter<i32>,即implements Iterator<Item = &i32>。也就是说,它不会产生i32s,它会产生&i32s。

    因此,在第一个和第二个示例中,k 实际上是绑定到&i32s,而不是i32s。当您指定闭包的类型时,您实际上指定了错误的类型。最后一个示例有效的原因是A 是一个模式,不是变量名&k实际上所做的是“解构”&i32,将i32 部分绑定到名为k 的变量。

    “无可辩驳”的部分仅仅意味着该模式必须始终有效。例如,你不能在 thingy 实现 Iterator<Option<_>> 的地方做 for Some(x) in thingySome(x) 不一定对迭代器中的每个元素都有效;因此,这是一个可反驳的模式

    【讨论】:

      【解决方案2】:

      许多迭代器实际上返回一个引用而不是一个值。可以肯定的是,您必须检查.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);
          }
      }
      

      这个X&i32(对i32 的引用),因此k 的类型为&i32

      这就是为什么在调用 print_me 时会出现错误:&i32 is passed where i32 is expected.


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

      1. 指定与print_me不同的类型:

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

        print_me(*k);
        
      3. 在循环中通过解构改变k的类型:

        for &k in my_array.iter() { ... }
        

      发生解构是因为 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)
          }
      }
      

      my_array 的类型是[(i32, i32)]:2 个i32 的元组数组。因此.iter() 的结果是Iterator<Item = &(i32, i32)> 类型:一个迭代器,指向一个2 元组i32 又名&(i32, i32) 的引用。

      当我们使用无可辩驳的模式&(j, k) 时,我们会解构元组,这样:

      • 第一个元素绑定到j(推断为i32类型,仅在i32Copy时有效)
      • 第二个元素绑定到k((推断为i32类型)

      jk 因此成为该元素内i32 的临时副本。

      【讨论】:

        猜你喜欢
        • 2022-12-17
        • 2021-08-30
        • 1970-01-01
        • 2019-01-05
        • 2011-05-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多