Ich schreibe eine Funktion, die über einen Vektor von Result iteriert und Erfolg zurückgibt, wenn alle erfolgreich waren, oder einen Fehler, wenn einer fehlgeschlagen ist. Einschränkungen in error::Error frustrieren mich und ich bin mir nicht sicher, wie ich sie umgehen soll. Derzeit habe ich so etwas wie:

let mut errors = Vec::new();            

for result in results {                             
     match result {                        
         Err(err) => errors.push(err),
         Ok(success) => { ... }
     }                                                                                                                                      
}

if errors.is_empty() {
    return Ok(())
else {
    return Err(MyErrorType(errors))
}

Das Problem bei meinem aktuellen Ansatz ist, dass ich nur einen Fehler als cause von MyErrorType festlegen kann und der description meines Fehlers ein statischer String sein muss, damit ich dies kann Enthält nicht die Beschreibungen der einzelnen Auslösefehler. Alle Fehler sind möglicherweise für den Anrufer relevant.

5
Michael 28 Dez. 2015 im 10:15

2 Antworten

Beste Antwort

Es gibt keine Konvention, die ich kenne, und tatsächlich hatte ich nie das Problem, mehrere Fehler gleichzeitig zu melden ...

... Davon abgesehen gibt es zwei Punkte, die Ihnen helfen können:

  1. Es gibt keine Einschränkung, dass die Beschreibung 'static String ist. Sie verwechseln wahrscheinlich &'static str und &str. In fn description(&self) -> &str ist die Lebensdauer von str mit der Lebensdauer von self (Lebensdauerelision) verknüpft, und daher erfüllt ein eingebettetes String die Einschränkungen

  2. Error ist eine Schnittstelle, um Fehler einheitlich zu behandeln. In diesem Fall war zwar nur ein einziges cause vorgesehen, dies schließt jedoch einen spezifischeren Typ nicht aus, um mehrere Ursachen zu aggregieren, und da Error Downcasting zulässt (Error::is, {{X4}) }, ...) Der spezifischere Typ kann vom Handler abgerufen und vollständig abgefragt werden

Daher würde ich vorschlagen, dass Sie einen neuen konkreten Typ erstellen, der ausschließlich dazu dient, mehrere Fehler (in einem Vec<Box<Error>>) zu speichern und die Error -Schnittstelle zu implementieren. Es liegt an Ihnen, über die Beschreibung zu entscheiden und sie freizulegen.

Mit einem einzigen Typ können Ihre Kunden leichter auf Downcasting testen als mit einer unbekannten (und möglicherweise mit der Zeit wachsenden) Anzahl potenzieller Downcasting-Ziele.

5
Matthieu M. 28 Dez. 2015 im 07:57

Erweitern Sie Punkt 1 von Matthieus guter Antwort ein wenig. Der Punkt, an dem Sie wahrscheinlich auf Probleme stoßen (ich weiß, dass ich es getan habe, als ich versucht habe, Error zu implementieren), ist, dass Sie ein dynamisches description() haben möchten.

// my own error type
#[derive(Debug)] struct MyError { value: u8 }

impl fmt::Display for MyError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            write!(f, "bummer! Got a {}", self.value)
    }
}

// I am now tempted to add the problematic value dynamically
// into the description, but I run into trouble with lifetimes 
// this DOES NOT COMPILE because the String I'm building
// goes out of scope and I can't return a reference to it
impl error::Error for MyError {
   fn description(&self) -> &str {
        &format!("can't put a {} here!", self.value)
   }
}

Lösung 1

Erstellen Sie nicht dynamisch description(). Verwenden Sie einfach eine statische str. Dies ist die die meisten Implementierungen von Error on github scheinen zu tun. Wenn Sie den Wert abrufen und anzeigen (oder protokollieren) müssen, können Sie jederzeit von Ihrem Typ MyError darauf zugreifen. Mit Display (das Sie für alle Fehlerimplikationen implementieren müssen) können Sie dynamische Zeichenfolgen erstellen.

Ich habe ein erfundenes Beispiel auf dem Spielplatz erstellt, das zeigt, wie mehrere verfolgt werden Fehler.

Lösung 2

(was Matthieu vorschlägt) Sie können die Fehlermeldung im Fehler selbst speichern.

#[derive(Debug)] struct MyError { value: u8, msg: String }

impl MyError {
    fn new(value: u8) -> MyError {
        MyError { value: value, msg: format!("I don't like value {}", value) }
    }
}

// now this works because the returned &str has the same lifetime
// as self
impl error::Error for MyError {
   fn description(&self) -> &str {
        &self.msg
    }
}
3
Paolo Falabella 28 Dez. 2015 im 15:51