2015-02-18 9 views
5

Ho un programma che più o meno simile a questaNon può uscire di contenuti preso in prestito quando prendere in prestito un tipo generico

struct Test<T> { 
    vec: Vec<T> 
} 

impl<T> Test<T> { 
    fn get_first(&self) -> &T { 
     &self.vec[0] 
    } 

    fn do_something_with_x(&self, x: T) { 
     // Irrelevant 
    } 
} 

fn main() { 
    let t = Test { vec: vec![1i32, 2, 3] }; 
    let x = t.get_first(); 
    t.do_something_with_x(*x); 
} 

In sostanza, noi chiamiamo un metodo sul struct Test che prende in prestito un certo valore. Quindi chiamiamo un altro metodo sulla stessa struttura, passando il valore ottenuto in precedenza.

Questo esempio funziona perfettamente. Ora, quando rendiamo il contenuto di main generico, non funziona più.

fn generic_main<T>(t: Test<T>) { 
    let x = t.get_first(); 
    t.do_something_with_x(*x); 
} 

allora ottengo il seguente errore:

error: cannot move out of borrowed content

src/main.rs:14 let raw_x = *x;

Io non sono del tutto sicuro perché questo sta accadendo. Qualcuno può spiegarmi perché Test<i32> non viene preso in prestito quando si chiama get_first mentre Test<T> è?

risposta

10

La risposta breve è che i32 implementa il tratto Copy ma non lo è T. Se si utilizza fn generic_main<T: Copy>(t: Test<T>), il problema immediato viene risolto.

La risposta più lunga è che Copy è un tratto speciale che significa che i valori possono essere copiati semplicemente copiando i bit. Tipi come i32 implementano Copy. Tipi come String do non implementano Copy perché, ad esempio, richiede un'allocazione dell'heap. Se hai copiato un numero String copiando i bit, ti ritroverei con due valori String che puntano alla stessa porzione di memoria. Quello non sarebbe buono (è pericoloso!).

Pertanto, il valore di Copy associato a T è piuttosto restrittivo. Un limite meno restrittivo sarebbe T: Clone. Il tratto Clone è simile a Copy (nel senso che copia i valori), ma di solito è composto da più di una semplice "copia di bit". Ad esempio, il tipo String implementerà Clone creando una nuova allocazione heap per la memoria sottostante.

Ciò richiede di modificare la modalità di generic_main è scritto:

fn generic_main<T: Clone>(t: Test<T>) { 
    let x = t.get_first(); 
    t.do_something_with_x(x.clone()); 
} 

In alternativa, se non si vuole avere sia i Clone o Copy limiti, allora si potrebbe cambiare il metodo di do_something_with_x di prendere una riferimento-T piuttosto che una proprietà T:

impl<T> Test<T> { 
    // other methods elided 

    fn do_something_with_x(&self, x: &T) { 
     // Irrelevant 
    } 
} 

E la tua generic_main soggiorni in gran parte lo stesso, tranne non si fanno dereference x:

fn generic_main<T>(t: Test<T>) { 
    let x = t.get_first(); 
    t.do_something_with_x(x); 
} 

Si può leggere di più su Copyin the docs. Ci sono alcuni buoni esempi, incluso come implementare Copy per i tuoi tipi.

+0

Risposta molto utile, molte grazie! – NSAddict

+0

L'uso di "moving bit" è un po 'confuso, poiché Rust si sposta spostando i bit. Forse "copiare i bit" sarebbe più chiaro? –

+0

@MatthieuM. Sicuro. Aggiornato.Per quel che vale, ho preso il testo precedente direttamente dal riferimento del linguaggio: "Copia, per i tipi" Plain Old Data "che possono essere copiati semplicemente spostando i bit." – BurntSushi5

Problemi correlati