2015-07-13 19 views
7

Sto provando a codificare un elenco generico come esercizio - so che è già supportato dalla sintassi, sto solo cercando di capire se posso codificare una struttura dati ricorsiva. A quanto pare, non posso. Sto colpendo un muro quando voglio accedere a più di un campo di un valore di struct di proprietà.Come associare più campi struct senza ottenere l'errore "usa valore spostato"?

Quello che sto facendo è fondamentalmente questo - mi definisco una struct che conterrà un elenco:

struct ListNode<T> { 
    val: T, 
    tail: List<T> 
} 

struct List<T>(Option<Box<ListNode<T>>>); 

Lista vuota è solo rappresentata da List(None).

Ora, voglio essere in grado di aggiungere a un elenco:

impl<T> List<T> { 
    fn append(self, val: T) -> List<T> { 
     match self { 
      List(None) => List(Some(Box::new(ListNode { val: val, tail: List(None) }))), 
      List(Some(node)) => List(Some(Box::new(ListNode { val: node.val, tail: node.tail.append(val) }))) 
     } 
    } 
} 

Ok, in modo tale che non riesce con un errore "usato valore mosso: nodo" alla node.tail con una spiegazione che si trattava spostato a "node.val". Questo è comprensibile.

Così, ho cercare modi per utilizzare più di un campo di una struct e ho trovato questo: Avoiding partially moved values error when consuming a struct with multiple fields

Grande, così lo farò:

List(Some(node)) => { 
    let ListNode { val: nval, tail: ntail } = *node; 
    List(Some(Box::new(ListNode { val: nval, tail: ntail.append(val) }))) 
} 

Beh, no, il nodo ancora viene spostato all'assegnazione ("errore: uso del valore spostato" nodo "" a "coda: ntail"). Apparentemente questo non funziona più come nel link.

ho provato anche con arbitri:

List(Some(node)) => { 
    let ListNode { val: ref nval, tail: ref ntail } = *node; 
    List(Some(Box::new(ListNode { val: *nval, tail: (*ntail).append(val) }))) 
} 

Questa volta la decostruzione passa, ma la creazione del nuovo nodo viene a mancare con "l'errore: non può uscire di contenuti in prestito".

Mi manca qualcosa di ovvio qui? In caso contrario, qual è il modo corretto per accedere a più campi di una struttura che non viene passata per riferimento?

Grazie in anticipo per qualsiasi aiuto!

EDIT: Oh, dovrei probabilmente aggiungere che sto usando Rust 1.1 stabile.

risposta

5

C'è una strana interazione con Box in corso. È necessario aggiungere un'istruzione intermedia che estrae la casella.

List(Some(node)) => { 
    let node = *node; // this moves the value from the heap to the stack 
    let ListNode { val, tail } = node; // now this works as it should 
    List(Some(Box::new(ListNode { val: val, tail: tail.append(value) }))) 
} 

Si noti che ho rinominato il tuo argomento di funzione per value, così ho potuto scrivere la destrutturazione il breve modulo senza rinominare.

Try it out in the playground.

+0

Grazie! Questo funziona davvero :) Perché Box funziona in questo modo, però? C'è una spiegazione ragionevole? – ebvalaim

+0

Sono abbastanza sicuro che abbia a che fare con il fatto che l'unboxing deref è un po 'magico per il compilatore. –

+1

ecco il bug corrispondente: https://github.com/rust-lang/rust/issues/16223 –