🤛

Rustでエラーを握りつぶす

2024/02/25に公開

エラーは握りつぶすのは良くないと思う(小学生並みの思考)ので、こんなもん個人用途で書き捨てる時にしか使わないと思いますよ。
書き捨てる時に、失敗するかもしれないけどunwrap()だと強制終了してくるから困る!!からその対処

?演算子を使う

ResultかOptionを返す関数には?演算子が使えます。?演算子の対象がSomeだったりOkだったりした場合は中身を取り出してくれて、ErrだったりNoneだったりした場合は早期リターンしてくれるものですね。エラー処理がだるい場合はこいつを使っておけば問題ありませんね

fn returns_result() -> Result<u32, Box<dyn std::error::Error>> {
    let mut string = String::new();
    std::io::stdin().read_line(&mut string)?;
    let number = string.parse::<u32>()?;

    Ok(number)
}

fn returns_option() -> Option<u32> {
    let mut v = vec![1, 2, 3, 4, 5].into_iter();

    let first = v.next()?;
    let second = v.next()?;
    let third = v.next();
    third
}

Optionを使う

Result型を返してくる関数も.ok()を使って全部Optionにします。これでエラー情報が皆無になりますね!(^v^)/

fn aiueo() -> Option<()> {
    let mut v = vec![1, 2, 3, 4, 5].into_iter();
    // `Option`を返すので単に`?`を付けるだけでいい
    let first = v.next()?;
    let second = v.next()?;

    let mut string = String::new();
    // `Result`型を返してくるので`.ok()`で`Option`型に変えて`?`
    std::io::stdin().read_line(&mut string).ok()?;
    let number = string.parse::<u32>().ok()?;

    Some(())
}

失敗するかもしれないイテレータ処理には.collect::<Option<V>>()が使えるので活用しましょう
(下のはHTTPのリクエストヘッダーをHashMapに変換する例(※書き捨て))

fn parse_headers(request_headers: Vec<String>) -> Option<HashMap<String, String>> {
    request_headers
        .into_iter()
        .map(|header| {
            let mut pair = header.split(": ").map(|x| x.to_string());
            match (pair.next(), pair.next()) {
                (Some(key), Some(value)) => Some((key, value)),
                _ => None,
            }
        })
        .collect::<Option<HashMap<String, String>>>()
}

で、こうみんながみんなOptionを返して伝播していって、最終的にmainくらいの上層で握りつぶします。matchでもunwrap_or_elseでもなんでも好きなように
エラーがあったらエラーがあったことくらいは情報として出してもいいかもですね

match aiueo() {
    None => eprintln!("エラー有り"),
    Some(_) => (),
};

Resultを使う

握りつぶしてるせいでどこでエラーが起きたかも分からねえじゃねえか!って思うのであればOptionを.ok_or()Resultに変換するのもありでしょう
「どこでエラーが起きたか」くらいの気持ち程度のエラー情報で

fn aiueo_result() -> Result<(), Box<dyn std::error::Error>> {
    let mut v = vec![1, 2, 3, 4, 5].into_iter();
    // `.ok_or()`でResultに変える
    let first = v.next().ok_or("vの1個目が無い")?;
    let second = v.next().ok_or("vの2個目がない")?;

    let mut string = String::new();
    std::io::stdin().read_line(&mut string)?;
    let number = string.parse::<u32>()?;

    Ok(())
}

fn main() {
    match aiueo_result() {
        Err(e) => println!("エラー有り:{}", e),
        Ok(()) => (),
    }
}

まとめ

マジで書き捨て用途以外に使わないと思う

GitHubで編集を提案

Discussion