当Rust抛出错误“无法移出共享引用后面的'*variable`”时,这意味着什么?

f5emj3cl  于 2023-02-16  发布在  其他
关注(0)|答案(2)|浏览(154)

我正在学习 rust ,我有这个代码,而下面的教程。所有这组代码所做的是循环通过的数字,并加起来的总和。

enum Shot {
    Bullseye,
    Hit(f64),
    Miss,
}

impl Shot {
    fn points(self) -> i32 {
        match self {
            Shot::Bullseye => 5,
            Shot::Hit(x) if x < 3.0 => 2,
            Shot::Hit(x) => 1,
            _ => 0,
        }
    }
}

fn main() {
    let mut shots: Vec<Shot> = Vec::new();

    shots.push(Shot::Bullseye);
    shots.push(Shot::Hit(5.0));
    shots.push(Shot::Miss);

    let mut total = 0;

    for shot in shots.iter() {
        let points = shot.points();
        total += points
    }

    println!("Total: {}", total);
}

但是,当我运行这个程序时,我得到了以下错误:

error[E0507]: cannot move out of `*shot` which is behind a shared reference
   |
68 |         let points = shot.points();
   |                      ^^^^^--------
   |                      |    |
   |                      |    `*shot` moved due to this method call
   |                      move occurs because `*shot` has type `Shot`, which does not implement the `Copy` trait
   |
note: this function takes ownership of the receiver `self`, which moves `*shot`
  --> src/main.rs:21:15
   |
21 |     fn points(self) -> i32 {
   |               ^^^^

奇怪的是,如果我把它改成这样,所有的编译都不会出错:

for shot in shots {
        let points = shot.points();
        total += points
    }

为什么当我移除迭代器时它还能工作?然而循环没有迭代器也能工作?
我尝试破译错误消息,但仍然不明白发生了什么。在这种情况下,我认为shot是一个引用。我正在调用shot.points()函数,该函数返回变量points拥有的i32值。然而,由于调用shot.points()函数,错误提到shot被移动?
错误的真正含义是什么?为什么会发生这种情况?

bfnvny8b

bfnvny8b1#

让我们尝试在循环中获取shot的实际类型:

for shot in shots.iter() {
    let foo: () = shot;
}

Playground
错误消息告诉我们shot的类型为&Shot。换句话说,它是对Shot的共享引用。但是points被定义为fn points (self) -> i32,这意味着它需要一个拥有的值,所以你不能在引用上调用shot.points()。正如其他人所指出的,修复很容易:只需更改points以获取参考,即fn points (&self) -> i32playground)。
现在,如果我们删除.iter(),它为什么会工作呢?看看在这种情况下类型会发生什么:

for shot in shots {
    let foo: () = shot;
}

Playground
现在shot的类型为Shot,没有引用,这是因为for shot in shots实际上等价于for shot in shots.into_iter(),后者消耗shots并迭代拥有的值,结果是在循环之后不能再使用它:

for shot in shots {
}
println!("{:?}", shots);

不起作用:

error[E0382]: borrow of moved value: `shots`
  --> src/main.rs:32:22
   |
20 |     let mut shots: Vec<Shot> = Vec::new();
   |         --------- move occurs because `shots` has type `Vec<Shot>`, which does not implement the `Copy` trait
...
28 |     for shot in shots {
   |                 ----- `shots` moved due to this implicit call to `.into_iter()`
...
32 |     println!("{:?}", shots);
   |                      ^^^^^ value borrowed here after move
   |
note: this function takes ownership of the receiver `self`, which moves `shots`
  --> /rustc/d5a82bbd26e1ad8b7401f6a718a9c57c96905483/library/core/src/iter/traits/collect.rs:262:18
   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider iterating over a slice of the `Vec<Shot>`'s content to avoid moving into the `for` loop
   |
28 |     for shot in &shots {
   |                 +
vojdkbi0

vojdkbi02#

函数points取得shot的所有权,所以在下一次迭代中,它不能再这样做了。
给出了使用shot所做的操作(仅阅读),您可以使用一个不可变引用来解决这个问题:

fn points(&self) -> i32 {

您还需要取消引用x

Shot::Hit(x) if *x < 3.0 => 2,

Rust playground link

相关问题