Почему присвоение элементу указателя все еще действует после перемещения указателя?

Почему 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));
}

Следующее не компилируется со знакомой ошибкой "использование перемещенного значения":

#[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
    };
}
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

Ответы

Ответ 1

Это не имеет никакого отношения к указанию или нет; это также работает:

#[derive(Debug)]
struct NonCopy;

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

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

    drop(foo);

    foo.name = NonCopy;
}

Хотя я не могу найти аналогичный SO-вопрос, который, как я знаю, ранее видел, это цитата из nikomatsakis описывает это:

В целом ходы отслеживаются на довольно узком уровне детализации. Мы намереваемся в конечном итоге разрешить вам "заполнить" оба поля, а затем снова использовать структуру. Наверное, сегодня это не работает. Мне нужно снова взглянуть на код ходов, но я думаю, что в целом одна из вещей, которые я хотел бы использовать в post 1.0, - это расширение системы типов, чтобы лучше справляться с вещами, которые были перенесены (в частности, я хочу поддержать перемещается из указателей мусора, пока вы восстанавливаете значение, прежде чем делать что-либо ошибочное). Во всяком случае, я думаю, что этот пример больше или меньше выпадает из рассмотрения вещей в целом, хотя вы могли бы представить себе правила, которые говорят "если вы двигаетесь f, вы никогда не сможете снова коснуться любых подполей f без восстановления f как единицы".

Также обсуждается подредактор Rust, который ссылается на проблему Rust 21232: "заимствование позволяет частично восстановить структуру, которая была удалена, но не использует ее"

Концептуально, есть флаг для каждого из полей в структуре в дополнение к самой структуре - мне нравится думать о аналогии картонной коробки Криса Моргана. Вы можете перейти из поля структуры, если вы вернетесь назад, прежде чем использовать struct:

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

println!("{:?}", foo);

Очевидно, что с 2014 года никто не потрудился приложить усилия для того, чтобы снова пометить всю структуру как действительную снова после повторного заполнения полей.

Реально, вам действительно не нужна эта функциональность, так как вы можете сразу назначить всю переменную. Текущая реализация чрезмерно безопасна, поскольку Rust препятствует вам делать что-то, что кажется ОК.