2016-03-26 37 views
8

C'è un grande esempio della semantica di movimento di Rust documentata qui: Rust Move Semantics sul sito web di Rust By Example.In che modo Rust sposta le variabili dello stack che non sono copiabili?

Ho una conoscenza di base di entrambi i casi dimostrati. Il primo è come una primitiva può avere un nuovo alias e l'originale può ancora essere utilizzato perché il risultato finale è una copia che vede come i32 utilizza il tratto Copy. Questo ha senso per me.

Inoltre, per molte buone ragioni il secondo esempio ha senso in termini di avere alias multipli che si riferiscono a uno i32 nell'heap. Ruggine applica le regole di proprietà e pertanto l'alias originale non può essere utilizzato ora che è stata creata una nuova associazione. Questo aiuta a prevenire gare di dati, doppie libere, ecc.

Ma sembrerebbe che ci sia un terzo caso di cui non si parla. In che modo Rust implementa le mosse di strutture allocate nello stack che non implementano il tratto Copy? Questo è illustrato con il seguente codice:

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

questo so: Nel caso precedente, ruggine allocherà la Employee sulla pila. La precedente struttura non implementa il tratto e pertanto non verrà copiato quando viene assegnato a un nuovo alias. Questo mi confonde molto perché se la struttura è allocata nello stack e non implementa anche il tratto dove/come si sposta? Si sposta fisicamente nel frame dello stack di do_something()?

Qualsiasi aiuto è apprezzato nello spiegare questo enigma.

+1

Ti dispiacerebbe semplificare il tuo esempio? Sarebbe bello rendere la struttura 'Dipendente' meno complessa e almeno rimuoverla. Ad esempio, 'struct Employee {age: i32}' sarebbe sufficiente. –

+0

@LukasKalbertodt - sì, ho semplificato l'esempio. –

risposta

6

Viene spostato fisicamente nello stack frame di do_something()?

Sì. I tipi non Copy sono spostati fisicamente esattamente come i tipi Copy sono: con un memcpy. Hai già capito che i primitivi tipi Copy vengono copiati nella nuova posizione (ad esempio, nuovo frame dello stack) byte per byte.

Ora consideriamo questa implementazione di Box:

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

Quando si dispone di

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

poi un i32 è allocato sul mucchio e la Box salva il puntatore prima per quel mucchio allocata la memoria. Notare che lo Box direttamente (il puntatore raw all'interno) è direttamente nello stack, non sull'heap! Solo lo i32 è nell'heap.

Quando lo Box viene spostato, è memcpy ed, come ho appena detto. Ciò significa che i contenuti dello stack vengono copiati (!!) ... quindi il puntatore viene copiato byte per byte. Non esiste una seconda versione di i32!

Non c'è differenza tra i tipi Copy e non Copy quando si tratta di muoversi fisicamente. L'unica differenza è che il compilatore applica regole diverse su questi tipi.

+4

Nit minore: AIUI, non vi è alcuna garanzia che i valori vengano * effettivamente * spostati, ma la semantica è tale che è necessario assumere che essi siano. Ad esempio, il compilatore è libero di riscrivere 'do_something' per accettare un riferimento a' b' per evitare di copiare bit in giro. L'importante è che il codice si comporti * come se * i bit fossero spostati. – Shepmaster