为什么在指针移动后分配给指针的成员仍然有效?

use*_*063 14 rust

为什么n1_mut在这个例子中仍然有效?它已经被移入,Option::Some所以它不应该是无效的吗?

struct MyRecordRec2<'a> {
    pub id: u32,
    pub name: &'a str,
    pub next: Box<Option<MyRecordRec2<'a>>>
}

#[test]
fn creating_circular_recursive_data_structure() {
    let mut n1_mut = MyRecordRec2 {
        id: 1,
        name: "n1",
        next: Box::new(None)
    };

    let n2 = MyRecordRec2 {
        id: 2,
        name: "n2",
        next: Box::new(Some(n1_mut))
    };

    //Why is n1_mut still valid?
    n1_mut.next = Box::new(Some(n2));
}
Run Code Online (Sandbox Code Playgroud)

以下不使用熟悉的"使用移动值"错误编译:

#[test]
fn creating_and_freezing_circular_recursive_data_structure() {
    let loop_entry = {
        let mut n1_mut = MyRecordRec2 {
            id: 1,
            name: "n1",
            next: Box::new(None),
        };

        let n2 = MyRecordRec2 {
            id: 2,
            name: "n2",
            next: Box::new(Some(n1_mut)),
        };

        n1_mut.next = Box::new(Some(n2));

        n1_mut
    };
}
Run Code Online (Sandbox Code Playgroud)
error[E0382]: use of moved value: `n1_mut`
  --> src/main.rs:44:9
   |
39 |             next: Box::new(Some(n1_mut)),
   |                                 ------ value moved here
...
44 |         n1_mut
   |         ^^^^^^ value used here after move
   |
   = note: move occurs because `n1_mut` has type `MyRecordRec2<'_>`, which does not implement the `Copy` trait
Run Code Online (Sandbox Code Playgroud)

She*_*ter 8

这与指针没有任何关系; 这也有效:

#[derive(Debug)]
struct NonCopy;

#[derive(Debug)]
struct Example {
    name: NonCopy,
}

fn main() {
    let mut foo = Example {
        name: NonCopy,
    };

    drop(foo);

    foo.name = NonCopy;
}
Run Code Online (Sandbox Code Playgroud)

虽然我找不到我之前见过的类似SO问题,但nikomatsakis的这句引言描述了它:

通常,移动以非常窄的粒度级别进行跟踪.我们打算最终允许您"填充"两个字段,然后再次使用该结构.我想这在今天不起作用.我必须再看一下移动代码,但我认为总的来说,我想要追求1.0版的一个原因是扩展类型系统以更好地处理已经移动的东西(特别是我想支持移出&mut指针,只要你在做任何错误之前恢复该值).无论如何,我认为这个例子或多或少都不会以一般的方式处理事物,尽管你可以想象规则说"如果你移动f,你再也不能触及f的任何子域而不将f恢复为一个单位".

还有关于Rust subreddit的讨论,它链接到Rust 问题21232:"借用检查器允许部分重新启动结构已被移走,但没有使用它"

从概念上讲,除了结构本身之外,结构中的每个字段都有一个标志 - 我喜欢考虑Chris Morgan的纸板盒类比.只要在使用结构之前重新进入,就可以移出拥有的struct的字段:

drop(foo.name);
foo.name = NonCopy;

println!("{:?}", foo);
Run Code Online (Sandbox Code Playgroud)

显然,自2014年以来,没有人愿意在重新填充字段后再次将整个结构标记为有效.

实际上,您并不真正需要此功能,因为您可以立即分配整个变量.当前的实现过于安全,因为Rust阻止你做一些似乎没问题的事情.