2017-10-19 2 views
14

Warum ist in diesem Beispiel n1_mut noch gültig? Es wurde in Option::Some verschoben, sollte es nicht ungültig sein?Warum ist die Zuweisung zu einem Mitglied eines Zeigers nach dem Verschieben des Zeigers noch gültig?

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)); 
} 

folgendem Grund nicht mit dem bekannten „Verwendung bewegt Wert“ Fehler nicht kompilieren:

#[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 
+0

Interessant. Ich bin mir nicht sicher, ob das als Fehler gilt - ich glaube nicht, dass Sie Unsicherheiten hervorrufen können, da es keine Möglichkeit gibt, die Erinnerung danach zu lesen. Aber wenn Sie einen rohen Zeiger auf den Stack behalten, können Sie sagen, dass 'n1Mut.next' tatsächlich gesetzt ist: https://play.rust-lang.org/?gist=d41422bfd142c289667e7c2fb3183be0&version=undefined – trentcl

+0

Interessanterweise ist es nicht möglich, sogar benutze 'n1_mut.next' danach. Außerdem fügt das Hinzufügen einer "Drop" -Implementierung Folgendes hinzu: "Fehler [E0383]: teilweise Reinitialisierung der nicht initialisierten Struktur' n1_mut' " –

Antwort

8

haben diese nichts mit dem Sein ein Zeiger oder nicht zu tun; Dies funktioniert auch:

#[derive(Debug)] 
struct NonCopy; 

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

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

    drop(foo); 

    foo.name = NonCopy; 
} 

Obwohl ich nicht die ähnliche SO Frage finden, dass ich weiß, dass ich vorher gesehen habe, diese quote from nikomatsakis beschreibt es:

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".

Es gibt auch Diskussionen über the Rust subreddit, die Links zu Rust issue 21232: "borrow-checker allows partial reinit of struct that has been moved away, but no use of it"

Konzeptionell gibt es ein Flag für jedes der Felder in einer Struktur zusätzlich zur Struktur selbst - ich denke gerne an Chris Morgan's cardboard box analogy. Sie können so lange aus einer in Besitz genommenen Struktur des Feldes bewegen, wie Sie in zurück bewegen, bevor die Struktur mit:

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

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

Offensichtlich seit 2014 hat niemand in der Bemühung zu setzen gestört Kennzeichnung die gesamte Struktur als gültig zu ermöglichen Sobald die Felder wieder gefüllt sind.

Realistisch benötigen Sie diese Funktionalität nicht wirklich, da Sie einfach die gesamte Variable auf einmal zuweisen können. Die aktuelle Implementierung ist zu sicher, da Rust verhindert, dass Sie etwas tun, das in Ordnung zu sein scheint.

Verwandte Themen