第60回です。前回はこちら。
[第60回の様子]
2022/12/07に第60回を開催した。
内容としてはRust By Example 日本語版の18. エラーハンドリングの「18.4.3. エラーをBoxする」〜「18.4.4. ?の他の活用法」に取り組んだ。
参加者は自分を入れて5人。師走で忙しいけどたくさん集まってよかった。
[学んだこと]
- 18.4.3. エラーをBoxする
- 前回は独自にDoubleErrorというエラー型を定義してOptionとParseIntErrorをまとめて扱う方法を学んだ
- ただ、元のエラー情報が失われているので、今回はBox型を利用して元のエラーを維持する方法を見ていく
- OptionはErrorトレイトを実装していないので、Errorトレイトを実装した独自の例外を定義する
use std::error; use std::fmt; // BoxでErrorをラップしたエイリアスを定義 type Result<T> = std::result::Result<T, Box<dyn error::Error>>; // OptionがNoneの時の例外を定義 #[derive(Debug, Clone)] struct EmptyVec; impl fmt::Display for EmptyVec { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "invalid first item to double") } } impl error::Error for EmptyVec {} // 最初の要素を2倍して返す関数 fn double_first(vec: Vec<&str>) -> Result<i32> { vec.first() .ok_or_else(|| EmptyVec.into()) // Boxに変換 .and_then(|s| { s.parse::<i32>() .map_err(|e| e.into()) // Boxに変換 .map(|i| 2 * i) }) } fn print(result: Result<i32>) { match result { Ok(n) => println!("The first doubled is {}", n), Err(e) => println!("Error: {}", e), } } fn main() { let numbers = vec!["42", "93", "18"]; let empty = vec![]; let strings = vec!["tofu", "93", "18"]; print(double_first(numbers)); // 82 print(double_first(empty)); // invalid first item to double print(double_first(strings)); // invalid digit found in string }
- OptionをBoxに変換する際にok_or()ではなくok_or_else()が使われているのは、Boxを使うことでの余計な動的メモリ確保を避けるため
- 18.4.4. ?の他の活用法
- double_first()の実装を
?
を使ってさらにシンプルにできる
// 前の例と同じ type Result<T> = std::result::Result<T, Box<dyn error::Error>>; // ?で内部の値をその場で取得 fn double_first(vec: Vec<&str>) -> Result<i32> { let first = vec.first().ok_or(EmptyVec)?; let parsed = first.parse::<i32>()?; Ok(2 * parsed) }
?
演算子はErr(From::from(err))
を実行してくれる- 上の例だと、EmptyVecの値を受け取って、
Box<dyn error::Error>
に変換している - 別の書き方として以下のような方法もあるがやや煩雑
fn double_first(vec: Vec<&str>) -> Result<i32> { let first = vec.first() // Box::from(EmptyVec)だとerror[E0283]: type annotations neededというコンパイルエラーになる .ok_or_else(|| Box::<dyn error::Error>::from(EmptyVec))?; let parsed = first.parse::<i32>()?; Ok(2 * parsed) }
[まとめ]
モブプログラミングスタイルでRust dojoを開催した。
久しぶりにメモリ確保に関する話が出てきて難しかった。
今週のプルリクエストはこちら。