2015-06-26 2 views
6

ich diese Schnipsel haben, die nicht die borrow Prüfung nicht besteht:Kann nicht als wandelbar mehr als einmal in einer Zeit, in einem Code ausleihen - kann aber in einem anderen sehr ähnlich

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() { 
} 

Mein Bauchgefühl ist, dass die Problem ist mit der Tatsache verbunden, dass HashMap entweder None oder eine Referenz des Wertes innerhalb der Datenstruktur zurückgibt. Aber ich will das nicht: meine Absicht ist, dass self.get_function entweder eine Byte-Kopie des gespeicherten Wertes oder einen Fehler zurückgeben sollte (deshalb setze ich .map(|f| *f), und Function ist Copy).

Das Ändern von &'a mut self zu etwas anderem hilft nicht.

jedoch folgende Schnipsel, ähnlich im Geiste, akzeptiert wird:

#[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); 
} 

So, jetzt ich bin verwirrt. Was ist das Problem mit dem ersten Snippet? (Und warum passiert es nicht in der Sekunde?)

Die Schnipsel sind Teil eines größeren Codes. Um mehr Kontext bereitzustellen, zeigt this auf dem Rust Playground ein vollständigeres Beispiel mit dem fehlerhaften Code und this zeigt eine frühere Version ohne HashMap, die den Border-Checker übergibt und normal ausgeführt wird.

+2

Ich glaube nicht, 'fun_name' sollte keine Lebensdauer haben. – o11c

+0

Ihr zweites Snippet hat überhaupt keine Lebensdauern, so dass es sich nicht wirklich für "ähnlich" qualifiziert –

Antwort

4

Sie sind in die Lebensfalle gefallen. Das Hinzufügen der gleichen Lebensdauer zu mehr Referenzen wird Ihr Programm mehr einschränken. Das Hinzufügen von mehr Lebensdauern und das Erteilen jeder Referenz für die minimal mögliche Lebensdauer wird mehr Programme erlauben. Wie @ o11c bemerkt, wird das Problem durch das Entfernen der Einschränkungen der 'a Lebensdauer behoben.

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(()) 
    } 
} 

Der Grund dieser Arbeiten ist, dass Rost neues Leben einfügt, so in dem Compiler Signaturen Ihre Funktionen werden wie folgt aussehen:

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> 

Versuchen Sie immer noch kein Lebensdauern und lassen Sie nutzen die Compiler ist schlau. Wenn das nicht gelingt, sprühen Sie nicht überall Leben, denken Sie darüber nach, wo Sie die Eigentumsrechte weitergeben möchten und wo Sie die Lebensdauer einer Referenz begrenzen möchten.

+0

Vielen Dank!Ich habe versucht, 'fn get_function <'b> (& 'b mut self, fun_name: &' b str) -> Ergebnis , Fehler> {' aber der Fehler wurde noch seltsamer. Es scheint, dass "Function" in dieser Methode nicht die gleiche Lebensdauer wie 'fun_name' haben kann, aber ich verstehe nicht wirklich * warum. – darque

+1

Ich weiß nicht, warum Sie die Lebensdauer einschränken möchten. Normalerweise versucht man, die Lebensdauern so weit wie möglich frei zu bekommen, der Compiler beschränkt dich genug. –

+1

Auch ich denke, die Lebensdauer Ihres 'name'-Feldes könnte" statisch "statt" a "sein. Wenn das nicht möglich ist, besteht eine gute Chance, dass das Feld stattdessen ein 'String' ist. Dasselbe gilt für den Schlüsseltyp der HashMap. –

7

Sie brauchen nur unnötige Lebensdauer Qualifier, um Ihren Code zu entfernen, zu kompilieren: in ihm (Function<'a>)

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

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

Ihr Problem war, dass man die Lebensdauer von &mut self und die Lebensdauer des Wertes gespeichert gebunden, was in den meisten Fällen unnötig ist. Mit dieser Abhängigkeit, die in get_function() Definition vorhanden war, musste der Compiler davon ausgehen, dass das Ergebnis des Aufrufs self.get_function(...)self borgt und es daher verbietet, dass Sie es erneut ausleihen.

Lebensdauer auf &str Argument ist auch unnötig - es beschränkt nur die möglichen Satz von Argumentwerten ohne Grund. Ihr Schlüssel kann eine Zeichenfolge mit beliebiger Lebensdauer sein, nicht nur 'a.

Verwandte Themen