kdnakt blog

hello there.

Rust dojo第67回を開催した

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

kdnakt.hatenablog.com

[第67回の様子]

2023/02/15に第67回を開催した。

内容としてはRust By Example 日本語版19. 標準ライブラリの型の「19.5.1. ?」、「19.6. panic!」に取り組んだ。
参加者は自分を入れて5人。今月から新しく入ってくれたメンバも続けて参加してくれているのでよかった。

[学んだこと]

  • 19.5.1. ?
  • 前回はResult型がOkかErrかをmatchを使って判別した結果、次のようなネストの深いコードになってしまった
enum MathError {
    DivisionByZero,
    NonPositiveLogarithm,
    NegativeSquareRoot,
}
type MathResult = Result<f64, MathError>;

// checkedモジュールの関数は上記MathResultを返す
fn op(x: f64, y: f64) -> f64 {
    match checked::div(x, y) {
        Err(why) => panic!("{:?}", why),
        Ok(ratio) => match checked::ln(ratio) {
            Err(why) => panic!("{:?}", why),
            Ok(ln) => match checked::sqrt(ln) {
                Err(why) => panic!("{:?}", why),
                Ok(sqrt) => sqrt,
            },
        },
    }
}
  • これでは扱いづらいので、?演算子を利用するとすっきり書ける
  • まずは?演算子を用いて目的の処理本体を記述する
// div() -> ln() -> sqrt()
fn op_(x: f64, y: f64) -> MathResult {
    let ratio = div(x, y)?; // Err型の場合ここで即return
    let ln = ln(ratio)?;
    sqrt(ln)
}

// ?を使わずに書くとこういうイメージ
fn op_(x: f64, y: f64) -> MathResult {
    let ratio = match div(x, y) {
        Ok(v) => v,
        Err(e) => return Err(From::from(e)),
    };
    let ln = match ln(ratio) {
        Ok(v) => v,
        Err(e) => return Err(From::from(e)),
    };
    sqrt(ln)
}
  • 続いて、上記関数を利用してエラーを処理する
pub fn op(x: f64, y: f64) {
    match op_(x, y) {
        Err(why) => panic!("{}", match why {
            MathError::NonPositiveLogarithm
                => "logarithm of non-positive number",
            MathError::DivisionByZero
                => "division by zero",
            MathError::NegativeSquareRoot
                => "square root of negative number",
        }),
        Ok(value) => println!("{}", value),
    }
}
  • エラーハンドリングと、関数本体のやりたいことが分離されて読みやすくなった
  • 19.6. panic!
  • panic!マクロはスレッドが所有権をもつ資源のデストラクタを呼び出しメモリから解放する
  • 次のように途中でpanicを引き起こす関数があると、それ以前の変数のデストラクタが呼ばれる
fn division(dividend: i32, divisor: i32) -> i32 {
    if divisor == 0 {
        panic!("division by zero");
    } else {
        dividend / divisor
    }
}

fn main() {
    // ヒープ上の整数
    let _x = Box::new(0i32);

    division(3, 0);

    println!("This point won't be reached!");

    // `_x`はここに到達する前に破棄される。
}
  • これはDropトレイトを利用しても確認することができる
struct A;

// デストラクタの実装
impl Drop for A {
    fn drop(&mut self) {
        println!("A is dropped");
    }
}

fn main() {
    let _a = A {};

    panic!("division by zero");

    println!("This point won't be reached!");
}

// 実行すると以下のようにデストラクタが呼ばれていることがわかる
// thread 'main' panicked at 'division by zero', src/main.rs:15:5
// note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
// A is dropped

[まとめ]

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

今週は比較的簡単だった気がする。次はハッシュマップが終わるとRcとかArcが出てくるので難しそう...。

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

github.com