2015-06-26 6 views
6

ho questo frammento che non passa il controllo prestito:non possono prendere in prestito come mutabile più di una volta in un momento in un codice -, ma può in un altro molto simile

use std::collections::HashMap; 

enum Error { 
    FunctionNotFound 
} 

#[derive(Copy, Clone)] 
struct Function<'a> { 
    name: &'a str, 
    code: &'a [u32] 
} 

struct Context<'a> { 
    program: HashMap<&'a str, Function<'a>>, 
    call_stack: Vec<Function<'a>>, 
} 

impl<'a> Context<'a> { 
    fn get_function(&'a mut self, fun_name: &'a str) -> Result<Function<'a>, Error> { 
     self.program.get(fun_name).map(|f| *f).ok_or(Error::FunctionNotFound) 
    } 

    fn call(&'a mut self, fun_name: &'a str) -> Result<(), Error> { 
     let fun = try!(self.get_function(fun_name)); 

     self.call_stack.push(fun); 

     Ok(()) 
    } 
} 

fn main() { 
} 

La mia sensazione è che il il problema è legato al fatto che HashMap restituisce None o un riferimento del valore all'interno della struttura dati. Ma non voglio questo: la mia intenzione è che self.get_function restituisca una copia in byte del valore memorizzato o un errore (ecco perché inserisco .map(|f| *f) e).

La modifica di &'a mut self in altro non aiuta.

Tuttavia, il seguente frammento, in qualche modo simile nello spirito, è accettato:

#[derive(Debug)] 
enum Error { 
    StackUnderflow 
} 

struct Context { 
    stack: Vec<u32> 
} 

impl Context { 
    fn pop(&mut self) -> Result<u32, Error> { 
     self.stack.pop().ok_or(Error::StackUnderflow) 
    } 


    fn add(&mut self) -> Result<(), Error> { 
     let a = try!(self.pop()); 
     let b = try!(self.pop()); 

     self.stack.push(a + b); 
     Ok(()) 
    } 
} 

fn main() { 
    let mut a = Context { stack: vec![1, 2] }; 
    a.add().unwrap(); 
    println!("{:?}", a.stack); 
} 

Così ora sono confuso. Qual è il problema con il primo snippet? (e perché non succede nel secondo?)

I frammenti fanno parte di un codice più grande. Per fornire più contesto, this su Rust Playground mostra un esempio più completo con il codice errato e this mostra una versione precedente senza HashMap, che supera il controllo del prestito e viene eseguito normalmente.

+2

Non credo che 'fun_name' debba avere una vita intera. – o11c

+0

il tuo secondo snippet non ha alcuna durata, quindi non si qualifica per "simile" –

risposta

4

Sei caduto nella trappola a vita. Aggiungere la stessa durata a più riferimenti limiterà di più il tuo programma. Aggiungendo più vite e dando ogni riferimento la durata minima possibile consentirà più programmi. Come note @ o11c, la risoluzione dei problemi relativi alla durata di 'a risolverà il problema.

impl<'a> Context<'a> { 
    fn get_function(&mut self, fun_name: &str) -> Result<Function<'a>, Error> { 
     self.program.get(fun_name).map(|f| *f).ok_or(Error::FunctionNotFound) 
    } 

    fn call(&mut self, fun_name: &str) -> Result<(), Error> { 
     let fun = try!(self.get_function(fun_name)); 

     self.call_stack.push(fun); 

     Ok(()) 
    } 
} 

Il motivo per cui funziona è che la ruggine inserisce nuove vite, così nel compilatore firme vostre funzioni sarà simile a questo:

fn get_function<'b>(&'b mut self, fun_name: &'b str) -> Result<Function<'a>, Error> 
fn call<'b>(&'b mut self, fun_name: &'b str) -> Result<(), Error> 

Cercate sempre di non utilizzare vite e lasciare che il il compilatore è intelligente Se fallisce, non spruzzare vite in tutto il mondo, pensa a dove vuoi passare la proprietà e dove vuoi limitare la durata di un riferimento.

+0

Grazie!Ho provato a mettere 'fn get_function <'b> (& 'b mut self, fun_name: &' b str) -> Risultato , Errore> {' ma l'errore è diventato ancora più strano. Sembra che 'Function' non possa avere la stessa durata di' fun_name' in questo metodo, ma in realtà non lo capisco * perché *. – darque

+1

Non so perché vorrai limitare la vita. Di solito cerchi di liberare il più possibile le vite, il compilatore ti sta costringendo abbastanza. –

+1

Inoltre suppongo che la durata del campo 'name' possa essere' 'static' invece di' 'a'. Se ciò non è possibile, ci sono buone probabilità che tu voglia che quel campo sia un 'String'. Lo stesso vale per il tipo di chiave 'HashMap'. –

7

Hai solo bisogno di rimuovere le qualificazioni a vita non necessaria in modo che il codice per compilare:

fn get_function(&mut self, fun_name: &str) -> Result<Function<'a>, Error> { ... } 

fn call(&mut self, fun_name: &str) -> Result<(), Error> { ... } 

Il tuo problema è che sei legato la durata del &mut self e la durata del valore memorizzato in esso (Function<'a>), che nella maggior parte dei casi non è necessario. Con questa dipendenza che era presente nella definizione get_function(), il compilatore ha dovuto presupporre che il risultato della chiamata self.get_function(...) prende in prestito self e quindi vieta di prenderlo nuovamente in prestito.

La durata dell'argomento &str non è necessaria, ma limita il possibile insieme di valori dell'argomento senza motivo. La tua chiave può essere una stringa con una durata arbitraria, non solo 'a.

Problemi correlati