2015-05-24 8 views
7

Ho appena scritto un piccolo programma Rust che calcola i numeri di Fibonacci e memoizza il calcolo. Funziona, ma sono un po 'confuso sul perché, specialmente sulla chiamata ricorsiva. (E 'anche probabilmente non è idiomatica.)Perché non è necessario prestare esplicitamente una variabile mutuata e mutuata?

Ecco il programma:

use std::collections::HashMap; 

fn main() { 
    let n = 42; // hardcoded for simplicity 
    let mut cache = HashMap::new(); 
    let answer = fib(n, &mut cache); 
    println!("fib of {} is {}", n, answer); 
} 

fn fib(n: i32, cache: &mut HashMap<i32,i32>) -> i32 { 
    if cache.contains_key(&n) { 
     return cache[&n]; 
    } else { 
     if n < 1 { panic!("must be >= 1") } 

     let answer = if n == 1 { 
      0 
     } else if n == 2 { 
      1 
     } else { 
      fib(n - 1, cache) + fib(n - 2, cache) 
     }; 
     cache.insert(n, answer); 
     answer 
    } 
} 

Ecco come ho capito quello che sta succedendo:

  • In main, let mut cache significa "Voglio essere in grado per mutare questa hashmap (o riassegnare la variabile) ".
  • Quando main chiama fib, passa &mut cache per dire "Ti sto prestando questo e ti è permesso di mutarlo".
  • nella firma di fib, cache: &mut Hashmap significa "mi aspetto di essere prestato un HashMap mutabile - di prendere in prestito con il permesso di mutare"

(Si prega di correggermi se sbaglio.)

Ma quando ricorre fib, chiamando fib(n -1, cache), non ho bisogno di usare fib(n -1, &mut cache), e ottengo un errore se lo faccio: "non posso prendere in prestito la variabile locale immutabile cache come mutabile". Eh? Non è una variabile locale immutabile, è un mutuo mutevole - giusto?

Se provo fib(n - 1, &cache), ottengo un errore di leggermente diverso:

error: mismatched types: 
expected `&mut std::collections::hash::map::HashMap<i32, i32>`, 
    found `&&mut std::collections::hash::map::HashMap<i32, i32>` 

che sembra che sta dicendo "Mi aspettavo un riferimento mutevole e ottenuto un riferimento a un riferimento mutabile".

So che fib è in prestito nella chiamata ricorsiva perché se ha dato la proprietà, non poteva chiamare in seguito cache.insert. E so che questo non è un caso speciale per la ricorsione, perché se definisco lo fib2 in modo che sia identico allo fib, posso farli recitare l'uno con l'altro e funziona perfettamente.

Perché non è necessario prestare esplicitamente una variabile mutuata in prestito?

+0

È possibile passare la proprietà e la funzione restituire una tupla con la risposta e la cache: [PlayPen] (http://is.gd/tUadZ0) –

risposta

10

I tuoi tre punti sono praticamente azzeccati. Quando il compilatore non ti permetterà di passare &mut cache, è perché il valore è già preso in prestito. Il tipo di cache è &mut HashMap<i32, i32>, pertanto il passaggio di &mut cache ha come risultato un valore di tipo &mut &mut HashMap<i32, i32>. Il solo passaggio dei risultati cache nel tipo previsto.

Il messaggio di errore specifico cannot borrow immutable local variable cache as mutable viene attivato perché la variabile cache non è di per sé modificabile, anche se la memoria a cui punta (lo HashMap) è. Questo perché la dichiarazione di argomento cache: &mut HashMap<i32, i32> non dichiara una variabile mut. Questo è simile al modo in cui un let differisce in mutabilità da un let mut. Rust supporta argomenti mutabili, che in questo caso apparirebbero come mut cache: &mut HashMap<i32, i32>.

+0

Hmm. Non sapevo di poter avere un valore mutabile in una variabile immutabile. Sei sicuro? 'let mut cache =' rende entrambi mutabili - esiste un modo per dichiarare solo la struttura mutabile? Inoltre, se aggiungo 'cache = & mut HashMap :: new();' all'inizio di 'fib', ottengo un errore a vita, ma non un errore" non puoi riassegnare questa variabile ". –

+0

Inoltre: interessante il fatto che "questo è un mutuo mutevole" viene controllato tramite un tipo. Grazie per avermi aiutato a capire. :) –

+0

@NathanLong In effetti è possibile avere un riferimento mutabile in una variabile immutabile. Questo perché Rust ha solo bisogno che il riferimento mutabile sia unico: nessun altro pezzo di codice può essere in grado di leggere o modificare il valore. Fa parte del controllore del prestito del compilatore per essere sicuro che tutti i 'e mut' siano garantiti come unici (lo stesso non è richiesto per' & '). Poiché le variabili sono sempre uniche (dato che le possiedi), un riferimento immutabile '& mut' è ancora univoco e può quindi essere modificato. Puoi anche controllare [questo] (https://doc.rust-lang.org/book/references-and-borrowing.html). :) – Snorre

Problemi correlati