问题 为什么在将移动值的成员分配给时,编译不会失败?


我正在研究一些例子 通过示例生锈

#[derive(Debug)]
struct Point {
    x: f64,
    y: f64,
}

#[derive(Debug)]
struct Rectangle {
    p1: Point,
    p2: Point,
}

fn main() {
    let mut point: Point = Point { x: 0.3, y: 0.4 };
    println!("point coordinates: ({}, {})", point.x, point.y);

    let rectangle = Rectangle {
        p1: Point { x: 1.0, y: 1.0 },
        p2: point,
    };

    point.x = 0.5; // Why does the compiler not break here,
    println!(" x is {}", point.x); // but it breaks here?

    println!("rectangle is {:?} ", rectangle);
}

我收到此错误(Rust 1.25.0):

error[E0382]: use of moved value: `point.x`
  --> src/main.rs:23:26
   |
19 |         p2: point,
   |             ----- value moved here
...
23 |     println!(" x is {}", point.x);
   |                          ^^^^^^^ value used here after move
   |
   = note: move occurs because `point` has type `Point`, which does not implement the `Copy` trait

我明白我给了 point 到了 Rectangle 对象,这就是为什么我不能再访问它,但为什么编译失败了 println! 而不是前一行的作业?


10502
2018-03-16 23:12


起源

较小的复制品。 - Shepmaster
我猜“使用移动的值”可以解释为您尝试读取它的值的点。只有当您尝试使用移动的值时,分配给它并不会真正改变Rust在“技术”意义上的保证。也就是说,我自己并不了解大部分内容,所以我很想听听更多人的意见。 - Simon Whitehead
感觉像compiller bug。更有趣的是 let p2 = point; point.x = 0.5; println!(" x is {}", p2.x); 编译精细并打印0.3,所以 point.x = 0.5; 没有 - qthree
@qthree这完全是预期的; Rust使用值类型,而不是引用类型。 - DK.


答案:


真正发生了什么

fn main() {
    let mut point: Point = Point { x: 0.3, y: 0.4 };
    println!("point coordinates: ({}, {})", point.x, point.y);

    drop(point);

    {
        let mut point: Point;
        point.x = 0.5;
    }

    println!(" x is {}", point.x);
}

事实证明,它已被称为 问题#21232


6
2018-03-17 04:08



为什么你有另一个 let mut point 在嵌套块内?这不会改变问题,因为你指定的点是一个新的点,在内存中与被删除的点不同的地方? - Alex Knauth
@AlexKnauth我认为这个想法是指出编译器看到它时发生了什么。也就是说,编译器会看到额外的范围,它有自己的“点”,允许赋值。 - Simon Whitehead
因为 point.x = 0.5; 在原始代码中没有任何移动值,但尝试初始化新值。 - qthree
但是在原始代码中,范围不存在,并且它没有创建新的 point 变量,但分配给旧的丢弃 point。或者我误解了这是如何工作的? - Alex Knauth
@AlexKnauth你是对的,原始代码中没有它。我认为qthree的目的是展示编译器看到的内容。编译器看到OP的代码就好像这个额外的范围就在那里。这就是作业的原因,但是 println! 线没有。 - Simon Whitehead


问题是编译器允许部分重新初始化结构,但之后整个结构不可用。即使结构只包含一个字段,即使您只是尝试读取刚刚重新初始化的字段,也会发生这种情况。

struct Test {
    f: u32,
}

fn main() {
    let mut t = Test { f: 0 };
    let t1 = t;
    t.f = 1;
    println!("{}", t.f);
}

这将在讨论中 问题21232


4
2018-03-18 09:55



我不清楚这个答案提供了什么 现有答案 - Shepmaster
@Shepmaster它删除了错误的部分。作为奖励,我添加了对该问题的简要说明,因为发布链接是不好的形式。 - starblue