Rust dojo第58回を開催した

第58回です。前回はこちら。

kdnakt.hatenablog.com

 

 

[第58回の様子]

2022/11/16に第58回を開催した。

 

内容としてはRust By Example 日本語版の18. エラーハンドリングの「18.3.1. Resultのmap」〜「18.3.2. Resultに対するエイリアス」に取り組んだ。

 

参加者は自分を入れて8人。ここ1ヶ月くらい、入れ替わりで安定して6人集まっていたのがたまたまほぼ全員集まってくれた形。

 

[学んだこと]

  • 18.3.1. Resultのmap
  • 基本的にpanic!()マクロを使うのではなく、呼び出し側にエラーハンドリングをさせるのが良い
  • Resultをmatchで処理するとErr(e) => Err(e)が以下のように連続する
use std::num::ParseIntError;

fn multiply(first_number_str: &str, second_number_str: &str) -> Result<i32, ParseIntError> {
    match first_number_str.parse::<i32>() {
        Ok(first_number)  => {
            match second_number_str.parse::<i32>() {
                Ok(second_number)  => {
                    Ok(first_number * second_number)
                },
                Err(e) => Err(e),
            }
        },
        Err(e) => Err(e),
    }
}
  • これを避けるにはmap()やand_then()を利用するとシンプルに書ける
use std::num::ParseIntError;

fn multiply(first_number_str: &str, second_number_str: &str) -> Result<i32, ParseIntError> {
    first_number_str.parse::<i32>().and_then(|first_number| {
        second_number_str.parse::<i32>().map(|second_number| first_number * second_number)
    })
}
  • ここでand_then()の中でmap()が呼ばれている
    • 両方map()にしてしまうと、以下のコンパイルエラーになってしまうので注意が必要
error[E0308]: mismatched types
  --> src/main.rs:10:5
   |
9  |   fn multiply(first_number_str: &str, second_number_str: &str) -> Result<i32, ParseIntError> {
   |                                                                   -------------------------- expected `Result<i32, ParseIntError>` because of return type
10 | /     first_number_str.parse::<i32>().map(|first_number| {
11 | |         second_number_str.parse::<i32>().map(|second_number| first_number * second_number)
12 | |     })
   | |______^ expected `i32`, found enum `Result`
   |
   = note: expected enum `Result<i32, _>`
              found enum `Result<Result<i32, ParseIntError>, _>`
// io::Error型を含むResult
pub type Result<T> = Result<T, Error>;
  • ParseIntErrorを例にとると次のようになる
use std::num::ParseIntError;

type AliasedResult<T> = Result<T, ParseIntError>;

fn multiply(first_number_str: &str, second_number_str: &str) -> AliasedResult<i32> {
    first_number_str.parse::<i32>().and_then(|first_number| {
        second_number_str.parse::<i32>().map(|second_number| first_number * second_number)
    })
}

fn print(result: AliasedResult<i32>) {
    match result {
        Ok(n)  => println!("n is {}", n),
        Err(e) => println!("Error: {}", e),
    }
}
  • これは例えば、i64にパースする関数があった場合、次のように流用できる
fn multiplyi64(first_number_str: &str, second_number_str: &str) -> AliasedResult<i64> {
    first_number_str.parse::<i64>().and_then(|first_number| {
        second_number_str.parse::<i64>().map(|second_number| first_number * second_number)
    })
}

fn printi64(result: AliasedResult<i64>) {
    match result {
        Ok(n)  => println!("n is {}", n),
        Err(e) => println!("Error: {}", e),
    }
}

fn main() {
    print(multiply("12345678901", "3332"));
    // i32を越えるので以下のエラーがプリントされる
    // Error: number too large to fit in target type

    printi64(multiplyi64("12345678901", "3332"));
    // i64に収まるので計算結果が表示される
    // n is 41135802098132
}
  • ここで、f32型用のメソッドも利用しようとしたが、型が合わずうまく既存のエイリアスにまとめられなかった
error[E0308]: mismatched types
  --> src/main.rs:15:5
   |
14 |   fn multiplyf32(first_number_str: &str, second_number_str: &str) -> AliasedResult<f32> {
   |                                                                      ------------------ expected `Result<f32, ParseIntError>` because of return type
15 | /     first_number_str.parse::<f32>().and_then(|first_number| {
16 | |         second_number_str.parse::<f32>().map(|second_number| first_number * second_number)
17 | |     })
   | |______^ expected struct `ParseIntError`, found struct `ParseFloatError`
   |
   = note: expected enum `Result<_, ParseIntError>`
              found enum `Result<_, ParseFloatError>`
  • Javaみたいな言語だとこういう時にcatch (ParseError e)みたいにまとめられて楽なのに、みたいな話をした
    • 例外を効率的に扱えるのが継承のある言語のいいところ、という指摘があってなるほどと思った。
  • RustではResultもいいけど最近は色々あってanyhowが鉄板という話も聞けた。今度触ってみよう

zenn.dev

qiita.com

 

[まとめ]

モブプログラミングスタイルでRust dojoを開催した。

例外処理はどの言語も大変だなあ...。

 

今週のプルリクエストはこちら。

github.com