サーチ…
前書き
備考
エラー処理の詳細についてはThe Rust Programming Language (The Book The Book)
共通の結果メソッド
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)
}
設定ファイルが存在しない場合は、次のように出力されます。
Error: Failed to read config file: No such file or directory (os error 2)
解析に失敗した場合は、次のように出力されます。
Error: Failed to parse the config string!
注:プロジェクトが成長するにつれて、エラーの起点と伝搬経路に関する情報を失うことなく、これらの基本的なメソッド( ドキュメント )でエラーを処理するのは面倒です。また、上記のように複数のエラータイプを処理するために、エラーを文字列に早く変換するのは間違いです。はるかに良い方法は、クレートのerror-chain
を使用することerror-chain
。
カスタムエラーの種類
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)
}
}
原因を繰り返す
エラーの根本的な原因を見つけるためにデバッグ目的に役立つことがよくあります。 std::error::Error
を実装するエラー値を調べるには:
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();
}
基本的なエラーの報告と処理
Result<T, E>
は、タイプT
意味のある結果を伴う成功した実行を示すOk(T)
と、タイプE
値によって記述される実行中の予期しないエラーの発生を示すErr(E)
2つの変形を有するenum
型である。 。
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)
}
詳細については、 docsも参照してください?
オペレーター。
標準ライブラリには、すべてのエラータイプの実装が推奨されるError
特性が含まれています。実装例を以下に示します。
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
}
注:一般的に、 Option<T>
はエラー報告に使用しないでください。 Option<T>
は、値が存在しない可能性があることを示し、単純な理由があることを示します。対照的に、 Result<T, E>
は、実行中の予期しないエラーを報告するために使用されます。特に、それらを区別するための複数の失敗モードがある場合に使用します。さらに、 Result<T, E>
は戻り値としてのみ使用されます。 ( 古い議論。 )