Why is n1_mut
still valid in this example? It has been moved into Option::Some
so shouldn't it be invalid?
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));
}
The following does not compile with the familiar "use of moved value" error:
#[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
This doesn't have anything to do with being a pointer or not; this works as well:
#[derive(Debug)]
struct NonCopy;
#[derive(Debug)]
struct Example {
name: NonCopy,
}
fn main() {
let mut foo = Example {
name: NonCopy,
};
drop(foo);
foo.name = NonCopy;
}
Although I can't find the similar SO question that I know I've seen before, this quote from nikomatsakis describes it:
In general moves are tracked at a pretty narrow level of granularity. We intend to eventually permit you to "fill" both fields back in and then use the structure again. I guess that doesn't work today. I have to go look again at the moves code, but I think in general one of the things I'd like to pursue post 1.0 is extending the type system to deal better with things that have been moved from (in particular I want to support moves out of &mut pointers, so long as you restore the value before doing anything fallible). Anyway I think this example more-or-less falls out of treating things in a general way, though you could imagine rules that say "if you move f, you can never again touch any subfields of f without restoring f as a unit".
There's also discussion on the Rust subreddit, which links to Rust issue 21232: "borrow-checker allows partial reinit of struct that has been moved away, but no use of it"
Conceptually, there's a flag for each of the fields in a struct in addition to the struct itself — I like to think of Chris Morgan's cardboard box analogy. You can move out of an owned struct's field so long as you move back in before using the struct:
drop(foo.name);
foo.name = NonCopy;
println!("{:?}", foo);
Evidently, since 2014, no one has bothered to put in the effort to enable marking the entire struct as valid again once the fields are re-filled.
Realistically, you don't really need this functionality as you can just assign the entire variable at once. The current implementation is overly-safe as Rust is preventing you from doing something that seems OK.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With