Rust
Fehlerbehandlung
Suche…
Einführung
Result<T, E>
, um behebbare Fehler während der Ausführung anzuzeigen. Nicht behebbare Fehler verursachen Panik , ein eigenes Thema.
Bemerkungen
Details zur Fehlerbehandlung werden in The Rust Programming Language (aka The Book) beschrieben.
Häufige Result-Methoden
use std::io::{Read, Result as IoResult};
use std::fs::File;
struct Config(u8);
fn read_config() -> IoResult<String> {
let mut s = String::new();
let mut file = File::open(&get_local_config_path())
// or_else closure is invoked if Result is Err.
.or_else(|_| File::open(&get_global_config_path()))?;
// Note: In `or_else`, the closure should return a Result with a matching
// Ok type, whereas in `and_then`, the returned Result should have a
// matching Err type.
let _ = file.read_to_string(&mut s)?;
Ok(s)
}
struct ParseError;
fn parse_config(conf_str: String) -> Result<Config, ParseError> {
// Parse the config string...
if conf_str.starts_with("bananas") {
Err(ParseError)
} else {
Ok(Config(42))
}
}
fn run() -> Result<(), String> {
// Note: The error type of this function is String. We use map_err below to
// make the error values into String type
let conf_str = read_config()
.map_err(|e| format!("Failed to read config file: {}", e))?;
// Note: Instead of using `?` above, we can use `and_then` to wrap the let
// expression below.
let conf_val = parse_config(conf_str)
.map(|Config(v)| v / 2) // map can be used to map just the Ok value
.map_err(|_| "Failed to parse the config string!".to_string())?;
// Run...
Ok(())
}
fn main() {
match run() {
Ok(_) => println!("Bye!"),
Err(e) => println!("Error: {}", e),
}
}
fn get_local_config_path() -> String {
let user_config_prefix = "/home/user/.config";
// code to get the user config directory
format!("{}/my_app.rc", user_config_prefix)
}
fn get_global_config_path() -> String {
let global_config_prefix = "/etc";
// code to get the global config directory
format!("{}/my_app.rc", global_config_prefix)
}
Wenn die Konfigurationsdateien nicht vorhanden sind, wird Folgendes ausgegeben:
Error: Failed to read config file: No such file or directory (os error 2)
Wenn die Analyse fehlgeschlagen ist, wird Folgendes ausgegeben:
Error: Failed to parse the config string!
Hinweis: Wenn das Projekt wächst, wird es umständlich, Fehler mit diesen grundlegenden Methoden ( docs ) zu behandeln, ohne dass Informationen zum Ursprung und Ausbreitungspfad von Fehlern verloren gehen. Außerdem ist es definitiv eine schlechte Praxis, Fehler vorzeitig in Strings zu konvertieren, um mehrere Fehlertypen wie oben dargestellt zu behandeln. Ein viel besserer Weg ist die Verwendung der Kistenfehler error-chain
.
Benutzerdefinierte Fehlertypen
use std::error::Error;
use std::fmt;
use std::convert::From;
use std::io::Error as IoError;
use std::str::Utf8Error;
#[derive(Debug)] // Allow the use of "{:?}" format specifier
enum CustomError {
Io(IoError),
Utf8(Utf8Error),
Other,
}
// Allow the use of "{}" format specifier
impl fmt::Display for CustomError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
CustomError::Io(ref cause) => write!(f, "I/O Error: {}", cause),
CustomError::Utf8(ref cause) => write!(f, "UTF-8 Error: {}", cause),
CustomError::Other => write!(f, "Unknown error!"),
}
}
}
// Allow this type to be treated like an error
impl Error for CustomError {
fn description(&self) -> &str {
match *self {
CustomError::Io(ref cause) => cause.description(),
CustomError::Utf8(ref cause) => cause.description(),
CustomError::Other => "Unknown error!",
}
}
fn cause(&self) -> Option<&Error> {
match *self {
CustomError::Io(ref cause) => Some(cause),
CustomError::Utf8(ref cause) => Some(cause),
CustomError::Other => None,
}
}
}
// Support converting system errors into our custom error.
// This trait is used in `try!`.
impl From<IoError> for CustomError {
fn from(cause: IoError) -> CustomError {
CustomError::Io(cause)
}
}
impl From<Utf8Error> for CustomError {
fn from(cause: Utf8Error) -> CustomError {
CustomError::Utf8(cause)
}
}
Durch Ursachen iterieren
Für Debugging-Zwecke ist es oft hilfreich, die Hauptursache eines Fehlers zu finden. Um einen Fehlerwert zu untersuchen, der std::error::Error
implementiert:
use std::error::Error;
let orig_error = call_returning_error();
// Use an Option<&Error>. This is the return type of Error.cause().
let mut err = Some(&orig_error as &Error);
// Print each error's cause until the cause is None.
while let Some(e) = err {
println!("{}", e);
err = e.cause();
}
Grundlegende Fehlerberichterstattung und -behandlung
Result<T, E>
ist ein enum
der zwei Varianten aufweist: Ok(T)
zeigt die erfolgreiche Ausführung mit einem aussagekräftigen Ergebnis vom Typ T
an und Err(E)
zeigt das Auftreten eines unerwarteten Fehlers während der Ausführung an, der durch einen Wert des Typs E
.
enum DateError {
InvalidDay,
InvalidMonth,
}
struct Date {
day: u8,
month: u8,
year: i16,
}
fn validate(date: &Date) -> Result<(), DateError> {
if date.month < 1 || date.month > 12 {
Err(DateError::InvalidMonth)
} else if date.day < 1 || date.day > 31 {
Err(DateError::InvalidDay)
} else {
Ok(())
}
}
fn add_days(date: Date, days: i32) -> Result<Date, DateError> {
validate(&date)?; // notice `?` -- returns early on error
// the date logic ...
Ok(date)
}
Weitere Informationen finden Sie in den Dokumenten ?
Operator.
Standard - Bibliothek enthält ein Error
, das alle Fehlertypen empfohlen werden , zu implementieren. Eine Beispielimplementierung ist unten angegeben.
use std::error::Error;
use std::fmt;
#[derive(Debug)]
enum DateError {
InvalidDay(u8),
InvalidMonth(u8),
}
impl fmt::Display for DateError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
&DateError::InvalidDay(day) => write!(f, "Day {} is outside range!", day),
&DateError::InvalidMonth(month) => write!(f, "Month {} is outside range!", month),
}
}
}
impl Error for DateError {
fn description(&self) -> &str {
match self {
&DateError::InvalidDay(_) => "Day is outside range!",
&DateError::InvalidMonth(_) => "Month is outside range!",
}
}
// cause method returns None by default
}
Hinweis: Im Allgemeinen sollte Option<T>
nicht für die Meldung von Fehlern verwendet werden. Option<T>
zeigt eine erwartete Möglichkeit des Nichtvorhandenseins eines Werts und einen einzigen einfachen Grund dafür an. Im Gegensatz dazu wird Result<T, E>
verwendet, um unerwartete Fehler während der Ausführung zu melden, insbesondere wenn mehrere Fehlerarten vorhanden sind, um zwischen ihnen zu unterscheiden. Außerdem wird Result<T, E>
nur als Rückgabewert verwendet. ( Eine alte Diskussion. )