2015-12-20 6 views
5

I parametri possono essere passati alle funzioni e modificati:Come posso modificare una porzione che è un parametro di funzione?

fn set_42(int: &mut i32) { 
    *int += 42; 
} 

fn main() { 
    let mut int = 0; 
    set_42(&mut int); 
    println!("{:?}", int); 
} 

Output:

42 

Ingenuamente cambiare il codice per utilizzare una fetta non riesce con un sacco di errori:

fn pop_front(slice: &mut [i32]) { 
    *slice = &{slice}[1..]; 
} 

fn main() { 
    let mut slice = &[0, 1, 2, 3][..]; 
    pop_front(&mut slice); 
    println!("{:?}", slice); 
} 

Output:

<anon>:2:14: 2:27 error: mismatched types: 
expected `[i32]`, 
    found `&[i32]` 
(expected slice, 
    found &-ptr) [E0308] 
<anon>:2  *slice = &{slice}[1..]; 
         ^~~~~~~~~~~~~ 
<anon>:2:14: 2:27 help: see the detailed explanation for E0308 
<anon>:2:5: 2:11 error: the trait `core::marker::Sized` is not implemented for the type `[i32]` [E0277] 
<anon>:2  *slice = &{slice}[1..]; 
      ^~~~~~ 
<anon>:2:5: 2:11 help: see the detailed explanation for E0277 
<anon>:2:5: 2:11 note: `[i32]` does not have a constant size known at compile-time 
<anon>:2:5: 2:11 note: the left-hand-side of an assignment must have a statically known size 
error: aborting due to 2 previous errors 

Se proviamo con una fetta mutabile (che non è quello che voglio veramente; Non voglio modificare i valori all'interno della fetta, voglio solo di modificare la fetta in sé in modo da coprire una gamma più ridotta di elementi) e un parametro mutevole, non ha alcun effetto sulla fetta originale:

fn pop_front(mut slice: &mut [i32]) { 
    slice = &mut {slice}[1..]; 
} 

fn main() { 
    let mut slice = &mut [0, 1, 2, 3][..]; 
    pop_front(&mut slice); 
    println!("{:?}", slice); 
} 

Output:

[0, 1, 2, 3] 

domanda: c'è un modo per modificare una fetta che è un parametro di funzione? Non voglio modificare gli elementi all'interno della sezione; Voglio solo modificare l'intervallo della fetta stessa in modo che diventi una "sottoscheda" più piccola.

risposta

5

Come altri hanno già detto, l'idea centrale qui è prendere un &mut &... [T] (dove ... è mut o vuoto) e lettura/scrittura alla porzione interna. Le altre risposte dimostrano che è possibile per &mut &[T] in codice sicuro, e possibile per &mut &mut [T] con codice non sicuro, ma non spiegano perché c'è la differenza ... e &mut &mut [T] è possibile anche con codice sicuro.

in termini espliciti-vita, il riferimento nidificato è qualcosa di simile &'a mut &'b ... [T] per alcune vite 'a e 'b, e l'obiettivo è quello di ottenere un &'b ... [T], affettarlo e scrivere che nella &'a mut.

Per &'a mut &'b [T], questo è facile: &[T] è copiare, in modo da scrivere in modo efficace *slice = &slice[1..] copiare il &'b [T] fuori dal &mut e poi, più tardi, sovrascrivere il valore esistente con quello più corto. La copia significa che uno letteralmente ottiene un &'b [T] con cui operare, e quindi non c'è alcuna connessione diretta tra questo e lo &'a mut, e quindi è legale mutare. Si è effettivamente qualcosa di simile

fn pop_front<'a, 'b>(slice: &'a mut &'b[i32]) { 
    // *slice = &slice[1..] behaves like 
    let value: &'b [i32] = *slice; 
    *slice = &value[1..] 
} 

(ho etichettato i tempi di vita e annotato il tipo di legare in mia spiegazione, ma questo non è richiesto per il codice a lavorare.)

Per &'a mut &'b mut [T] le cose sono un non è possibile copiare il dereferenziamento: il dereferenziamento non verrà copiato, verrà reindirizzato per fornire un valore &'a mut [T] ovvero lo slice ha una durata che è connessa allo esterno&'a mut, non al numero interno &'b mut [T]. Ciò significa che il riferimento affettato ha una durata inferiore rispetto al tipo che sta tentando di sovrascrivere, quindi non è valido memorizzare la sezione in quella posizione. In altre parole:

fn pop_front<'a, 'b>(slice: &'a mut &'b mut [i32]) { 
    let value: &'a mut [i32] = &mut **slice; 
    *slice = &mut value[1..] // error 
} 

Il modo per farlo in modo sicuro per &'a mut &'b mut [T] è quello di ottenere la fetta interna di riferimento con quella 'b vita. Ciò richiede di tenere traccia della regola del "proprietario unico", senza prendere in prestito e la migliore funzione per questo tipo di manipolazione della proprietà è mem::replace. Ci consente di estrarre lo spazio interno &'b mut [T] scambiandolo con un segnaposto, che possiamo quindi sovrascrivere con la versione breve.Il segnaposto migliore/solo è un array vuoto: la scrittura &mut [] può essere un per qualsiasi tipo X e qualsiasi durata 'c, poiché non ci sono dati da memorizzare e quindi non è necessario inizializzare e nessun dato sarà mai invalidato. In particolare, può essere un &'b mut [T]:

fn pop_front<'a, 'b>(slice: &'a mut &'b mut [i32]) { 
    let value: &'b mut [i32] = mem::replace(slice, &mut []); 
    *slice = &mut value[1..] 
} 

(cose Come sopra, che ho fatto più esplicito di quanto necessario.)

+0

Grazie mille per la spiegazione dettagliata e l'annotazione delle vite. È incredibilmente utile! – Cornstalks

3

Se è necessario modificare una sezione immutabile, see Cornstalks's answer.

Non è possibile modificare una sezione mutabile in Rust sicuro. Quando prendi una sottosezione di una sezione mutabile, prendi effettivamente in prestito dalla slice originale. Ciò significa che il subslice non deve sopravvivere alla sezione originale.

si desidera qualcosa che assomiglia a questo:

fn pop_front(slice: &mut &mut [i32]) { 
    *slice = &mut slice[1..]; 
} 

ma il subslice slice[1..] è valida solo fino alla fine della funzione, e quel punto il prestito finirà e la fetta originale (il parametro slice) sarà essere di nuovo utilizzabile

possiamo utilizzare un codice unsafe per costruire manualmente la fetta che vogliamo:

use std::slice; 

fn pop_front(slice: &mut &mut [i32]) { 
    let ptr = slice.as_mut_ptr(); 
    let len = slice.len(); 
    *slice = unsafe { slice::from_raw_parts_mut(ptr.offset(1), len - 1) }; 
} 

fn main() { 
    let mut slice = &mut [0, 1, 2, 3][..]; 
    pop_front(&mut slice); 
    println!("{:?}", slice); 
} 

playground

Questo programma uscite:

[1, 2, 3] 
3

utilizzando parte Francis Gagné's answer (non l'ho fatto pensare di provare &mut &), sono riuscito a farlo funzionare senza utilizzare il codice unsafe:

fn pop_front(mut slice: &mut &[i32]) { 
    *slice = &slice[1..]; 
} 

fn main() { 
    let mut slice = &[0, 1, 2, 3][..]; 
    pop_front(&mut slice); 
    println!("{:?}", slice); 
} 

Output:

[1, 2, 3] 
+0

Questa muta una fetta d'immutabile, non una fetta mutevole. Non è chiaro dalla domanda quale fosse il bisogno dell'OP ... –

+0

@ FrancisGagné: questo è quello che stavo cercando di chiedere nella mia domanda, anche se a dire il vero sto usando delle sezioni mutabili nel mio codice reale, quindi la tua risposta è stata molto utile lì. Stavo attraversando un periodo difficile per cercare di parlare sensibilmente di sezioni mutabili immutabili nella mia domanda, perché "mutabile immutabile" sembra privo di senso (ma probabilmente è il termine corretto). – Cornstalks

+0

Il tuo 'pop_front' prende un * riferimento * mutevole * a una * fetta * immutabile. Il mio 'pop_front' prende un riferimento mutabile a una sezione mutabile. –

Problemi correlati