Rustのエラーハンドリング

Result<T, E>

Result<T, E> 型は エラーになる可能性を示す列挙型で、処理が成功した場合の値はOk、処理が失敗した場合の値はErrで包み込む。

pub enum Result<T, E> {
Ok(T),
Err(E),
}

エラーをStringで返す

1つの関数の中で型の異なるエラーが発生する可能性がある場合、エラーの型をまとめたくなる。短いプログラムであれば、Result<T, E>のEをStringにして返す手段が考えられる。
以下のコードは複数の異なる型のエラーをまとめてStringに変換し、関数の呼び出し元にエラーの情報を伝えている。ただし、どのエラーもStringで返すため、エラーが発生したもともとの型(std::num::ParseIntError、もしくはstd::io::Error)の情報は失われてしまい、関数の呼び出し元で型情報による処理の分岐ができないという問題がある。

use std::fs::File;
use std::io::Read;
use std::path::Path;
fn file_double<P: AsRef<Path>>(file_path: P) -> Result<i32, String> {
let mut file = File::open(file_path).map_err(|e| e.to_string())?;
let mut contents = String::new();
file.read_to_string(&mut contents)
.map_err(|e| e.to_string())?;
let n: i32 = contents.trim().parse::<i32>().map_err(|e| e.to_string())?;
Ok(2 * n)
}
fn main() {
match file_double("foobar") {
Ok(n) => println!("{}", n),
Err(err) => println!("Error: {:?}", err),
}
}
rustc 1.27.0-nightly (79252ff4e 2018–04–29)

独自のエラー型を定義する

この節では、独自のエラーの型を定義することで、上の”エラーをStringで返す”に書いた問題を解決しようとしてる。 以下のコードはNetwork Programming with Rustで公開されているコードを使用している。各ライブラリのバージョンはCargo.tomlから確認できる。error.rs というファイルにエラーに関する処理がまとめている。

use std::error::Error;
use std::convert::From;
use std::fmt;
use diesel::result::Error as DieselError;
use rocket::http::Status;
use rocket::response::{Response, Responder};
use rocket::Request;
#[derive(Debug)]
pub enum ApiError {
NotFound,
InternalServerError,
}
impl fmt::Display for ApiError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
ApiError::NotFound => f.write_str("NotFound"),
ApiError::InternalServerError => f.write_str("InternalServerError"),
}
}
}
impl From<DieselError> for ApiError {
fn from(e: DieselError) -> Self {
match e {
DieselError::NotFound => ApiError::NotFound,
_ => ApiError::InternalServerError,
}
}
}
impl Error for ApiError {
fn description(&self) -> &str {
match *self {
ApiError::NotFound => "Record not found",
ApiError::InternalServerError => "Internal server error",
}
}
}
impl<'r> Responder<'r> for ApiError {
fn respond_to(self, _request: &Request) -> Result<Response<'r>, Status> {
match self {
ApiError::NotFound => Err(Status::NotFound),
_ => Err(Status::InternalServerError),
}
}
}
  1. 独自のエラー型を定義
  2. Errorトレイトを実装
  3. Fromトレイトを実装

独自のエラー型を定義

pub enum ApiError {
NotFound,
InternalServerError,
}

Errorトレイトを実装

impl Error for ApiError {
fn description(&self) -> &str {
match *self {
ApiError::NotFound => "Record not found",
ApiError::InternalServerError => "Internal server error",
}
}
}

Fromトレイトを実装

impl From<DieselError> for ApiError {
fn from(e: DieselError) -> Self {
match e {
DieselError::NotFound => ApiError::NotFound,
_ => ApiError::InternalServerError,
}
}
}

参考

--

--

Software engineer

Love podcasts or audiobooks? Learn on the go with our new app.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store