2016-03-26 3 views
8

Es gibt ein großartiges Beispiel für die Umzugssemantik von Rust, die hier dokumentiert ist: Rust Move Semantics auf der Rust By Example-Website.Wie verschiebt Rust Stack-Variablen, die nicht kopierbar sind?

Ich habe ein grundlegendes Verständnis beider Fälle demonstriert. Der erste Grund dafür, wie ein Grundelement einen neuen Alias ​​und das Original haben kann, kann weiterhin verwendet werden, da das Endergebnis eine Kopie ist, die als i32 die Eigenschaft Copy verwendet. Das macht Sinn für mich.

Darüber hinaus macht das zweite Beispiel aus vielen guten Gründen Sinn in Bezug auf mehrere Aliase, die sich auf einen i32 auf dem Heap beziehen. Rust erzwingt Eigentumsregeln und daher kann der ursprüngliche Alias ​​jetzt nicht verwendet werden, da eine neue Bindung erstellt wurde. Dies hilft, Datenrennen, Doppelfreigaben usw. zu verhindern.

Aber es scheint, dass es einen dritten Fall gibt, über den nicht gesprochen wird. Wie implementiert Rust Bewegungen von Stack-allokierten Strukturen, die das Merkmal Copy nicht implementieren? Dies wird mit dem folgenden Code dargestellt:

#[derive(Debug)] 
struct Employee{ 
    age: i32, 
} 

fn do_something(m: Employee){ 
    println!("{:?}", m); 
} 

fn main() { 
    let x = Employee { 
     age: 25, 
    }; 

    do_something(x); 

    //compiler error below because x has moved 
    do_something(x); 
} 

Das weiß ich: Im obigen Fall Rust die Employee auf dem Stapel zuordnen. Die obige Struktur implementiert das Copy Merkmal nicht und wird daher nicht kopiert, wenn sie einem neuen Alias ​​zugewiesen wird. Dies ist sehr verwirrend für mich, wenn die Struktur Employee auf dem Stapel zugeordnet ist und auch nicht die Copy Eigenschaft implementiert, wo/wie bewegt es sich? Wird es physisch in den Stack-Frame do_something() verschoben?

Jede Hilfe wird geschätzt, dieses Rätsel zu erklären.

+1

Würde es Ihnen etwas ausmachen, Ihr Beispiel zu vereinfachen? Wäre toll, die "Employee" -Struktur weniger komplex zu machen und zumindest die Lebensdauer zu entfernen. Zum Beispiel wäre 'struct Employee {age: i32}' genug. –

+0

@LukasKalbertodt - ja ich habe das Beispiel vereinfacht. –

Antwort

6

Wird es physikalisch in den Stack-Frame do_something() verschoben?

Ja. Nicht-Copy Typen werden physikalisch genau wie Copy Typen verschoben: mit einem memcpy. Sie haben bereits verstanden, dass primitive Copy -Typen Byte für Byte in den neuen Speicherort kopiert werden (z. B. neuer Stapelrahmen).

Betrachten wir nun diese Implementierung von Box:

struct Box<T> { 
    ptr: *const T, 
} 

Wenn Sie

let b = Box::new(27i32); 
do_something(b); // `b` is moved into `do_something` 

haben dann ein i32 auf dem Heap zugeordnet ist und die Box speichert die rohen Zeiger auf diesen Haufen zugewiesenen Speicher. Beachten Sie, dass die Box direkt (der rohe Zeiger innerhalb) direkt auf dem Stapel ist, nicht auf dem Heap! Nur die i32 ist auf dem Haufen. Wenn die Box verschoben wird, ist es memcpy Ed, wie ich gerade sagte. Dies bedeutet, dass der Stack-Inhalt kopiert wird (!!) ... also wird nur der Zeiger Byte für Byte kopiert. Es gibt keine zweite Version des i32!

Es gibt keinen Unterschied zwischen den Typen Copy und nicht Copy, wenn es um die physische Bewegung geht. Der einzige Unterschied besteht darin, dass der Compiler unterschiedliche Regeln für diese Typen erzwingt.

+4

Minor nit: AIUI, es gibt keine Garantie, dass die Werte * tatsächlich * verschoben werden, aber die Semantik ist so, dass Sie davon ausgehen müssen, dass sie es sind. Zum Beispiel kann der Compiler "do_something" umschreiben, um einen Verweis auf "b" zu akzeptieren, um das Kopieren von Bits zu verhindern. Wichtig ist, dass sich der Code verhält *, als ob * die Bits verschoben worden wären. – Shepmaster

Verwandte Themen