2015-06-27 16 views
12

In primo luogo, lasciare che il codice parla:non si può prendere in prestito `self.x` come immutabile, perché` * self` è anche preso in prestito come mutabile

#[derive(Debug)] 
struct Bar; 

#[derive(Debug)] 
struct Qux { 
    baz: bool 
} 

#[derive(Debug)] 
struct Foo { 
    bars: Vec<Bar>, 
    qux: Qux, 
} 

impl Foo { 
    fn get_qux(&mut self) -> &mut Qux { 
     &mut self.qux 
    } 

    fn run(&mut self) { 
     // 1. Fails: 
     let mut qux = self.get_qux(); 

     // 2. Works: 
     // let mut qux = &mut Qux { baz: false }; 

     // 3. Works: 
     // let mut qux = &mut self.qux; 

     let qux_mut = &mut qux; 
     qux_mut.baz = true; 

     for bar in &self.bars { 
      println!("{:?}", bar); 
     } 
    } 
} 

fn main() { 
    println!("Hello, world!"); 

    let mut foo = Foo { bars: vec!(), qux: Qux { baz: false } }; 
    foo.run(); 
} 

Questo errori:

error[E0502]: cannot borrow `self.bars` as immutable because `*self` is also borrowed as mutable 
    --> src/main.rs:33:21 
    | 
22 |   let mut qux = self.get_qux(); 
    |      ---- mutable borrow occurs here 
... 
33 |   for bar in &self.bars { 
    |      ^^^^^^^^^ immutable borrow occurs here 
... 
36 |  } 
    |  - mutable borrow ends here 

Se Decommenta sia 2. o 3., perché si compila bene? La funzione chiamata in 1. non fa nulla di radicalmente diverso da 2. o 3.. Allora, perché è impossibile compilare 1.?

Sebbene siano presenti many similar titled questions, non è stato possibile identificarlo chiaramente come duplicato (a parte il messaggio di errore che è lo stesso), probabilmente a causa della mia mancanza di comprensione del sistema di proprietà/prestito in Rust.

risposta

8

In Rust, il compilatore si arresta al limite della chiamata di funzione durante la valutazione dei parametri generici, che include parametri generici di durata. Nel tuo caso 1, si sta chiamando un metodo:

fn get_qux(&mut self) -> &mut Qux { 
    &mut self.qux 
} 

Questa funzione indica che tutte di self saranno presi in prestito mutably, e che il riferimento restituito vivrà più a lungo self volontà. Durante questo periodo, nessun altro prende in prestito (mutabile o no) di sé o dei suoi componenti.

Nel secondo caso, si crea uno Qux completamente nuovo che non ha alcun allegato alla propria struttura. Non è un grande esempio, perché ha un significato molto diverso. Se questo caso funziona per te, dovresti farlo. Tuttavia, non modificherete la stessa cosa del caso 1.

Nel terzo caso, si evita la chiamata di funzione. Ciò significa che il compilatore ha un po 'più di informazioni su cosa è esattamente preso in prestito. Nello specifico, può vedere che self.qux non interagisce affatto con self.bars, quindi non c'è alcun errore.

È possibile rendere il vostro lavoro esempio originale con l'aggiunta di un nuovo ambito:

fn run(&mut self) { 
    { 
     let mut qux = self.get_qux(); 
     let qux_mut = &mut qux; 
     qux_mut.baz = true; 
    } 

    for bar in &self.bars { 
     println!("{:?}", bar); 
    } 
} 

Qui, il campo di applicazione artificiale definisce chiaramente dove finisce il prestito mutevole. Una volta che il prestito è finito, altri articoli sono autorizzati a fare nuovi prestiti.

Se avete bisogno di modificare qux all'interno del ciclo, allora si sono tenuti a seguire il terzo modello:

let mut qux = &mut self.qux; 

for bar in &self.bars { 
    qux.baz = ! qux.baz; 
    println!("{:?}", bar); 
} 

O le più semplici:

for bar in &self.bars { 
    self.qux.baz = ! self.qux.baz; 
    println!("{:?}", bar); 
} 

Molte volte, è possibile refactoring del codice per creare nuove strutture che hanno informazioni e incapsulare un bel limite di mutazione per creare un codice come questo.

+3

Grazie per la tua risposta completa. Mi chiedo, tuttavia, come sia possibile utilizzare 'qux' all'interno del ciclo for se necessario? –

+0

@FelixSchlitter aggiornato. – Shepmaster

+1

Vedo. Ma diciamo che la funzione aveva qualche logica in più, ad es. ci è voluto un id e ho recuperato 'qux' da un vec di Qux, quindi avrei dovuto aggiungere quella logica ovunque. Forse ho bisogno di ricorrere a una macro? ...So che i tipi 'HashMap' e' Vec' hanno un metodo 'get_mut', forse c'è anche qualcosa da imparare dalla loro implementazione. Dovrò fare un'altra immersione. –

Problemi correlati