kdnakt blog

hello there.

Rust dojo第30回を開催した

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

kdnakt.hatenablog.com

 

 

[第30回の様子]

2022/03/09に第30回を開催した。

 

内容としてはRust By Example 日本語版の「9.4. Diverging functions」、「10. モジュール」、「10.1. プライベートとパブリック」を途中まで取り組んだ。

 

参加者は6人。先週より1人増えた🤗

 

dojo終了後、参加者のSlackの分報チャンネルがカリー=ハワード同型対応で盛り上がった結果、それを見た参加してない同僚たちも「懐かしい...(大学でやった)」、「三段論法みたいなやつ」、「今月末に講座があるよ(下記リンク参照)」などと盛り上がっていた。

kyoto-logic.hatenablog.com

 

[学んだこと]

  • 9.4. Diverging functions
  • まだ翻訳されていないページだったので、dojoのメンバーが出してくれている翻訳プルリクエストを参考にしながらモブプロを進めた
  • 発散しない関数は以下のようにリターンしない関数で、戻り値の型が!になる
fn foo() -> ! {
    // この呼び出しは決してリターンしない。
    panic!("This call never returns.");
}
  • !は空の型であり、インスタンス化できず、値を持たない
    • ()(Unit型)は値が1つだけあり、!とは別の型である
  • 変数宣言で()!を利用すると以下のようになる
// Compilation succeeds only in nightly version.
#![feature(never_type)]
fn some_fn() {
    ()
}

fn main() {
    let a: () = some_fn();
    // この関数はリターンするので、この行は実行される。
    println!("This function returns and you can see this line.")

    // この呼び出しは決してリターンしない。
    let x: ! = panic!("This call never returns.");
    // 従ってこの行は決して実行されない。
    println!("You will never see this line!");
}
  • 具体的にこれどういう時に使うんだろう?と思ったら、空集合だからどんな型にもキャストできる、という特性を生かしてこんなことができるらしい
fn sum_odd_numbers(up_to: u32) -> u32 {
    let mut acc = 0;
    for i in 0..up_to {
        let addition: u32 = match i%2 == 1 {
            true => i,
            false => continue, // continueはリターンしないので、変数additionが要求する型に違反しない
        };
        acc += addition;
    }
    acc
}

println!("Sum of odd numbers up to 9 (excluding): {}", sum_odd_numbers(9));
// 16が出力される(1+3+5+7)
  • 他にも、ネットワークサーバのような永遠にループする関数やプロセスを終了する関数の戻り値としても!が利用されるらしい
  • ここから、実際に!を引数の型に指定したりできるのだろうか?という話になり、試行錯誤の結果以下のexplosion関数が実装できた
#![feature(never_type)] // Compilation succeeds only in nightly version.

fn explosion<T>(a: !) -> T {
    a
}

fn main() {
    for i in 0..10 {
        println!("{}", explosion::<i32>(continue));
    }
}

// 何も出力されない。continueでforの先頭に戻るのでprintlnが実行されていない
  • こういう型の関係をカリー=ハワード同型対応という?らしいことを教えてもらった。むつかしい...

ja.wikipedia.org

mod my_mod {
  // これはプライベートな関数なのでモジュール外からはアクセス不可
  fn private_function() {
    println!("called `my_mod::private_function()`");
  }
}
  • pub修飾子を付けるとパブリックになる=モジュールの外から呼び出せる

mod my_mod { pub fn function() { println!("called `my_mod::function()`"); } } fn main() { // モジュール内のパブリックな関数を呼び出している my_mod::function(); }

  • モジュール内モジュールの場合、可視性の範囲をより詳細に宣言できる
mod my_mod {
  pub mod nested {
    // pub(in path)で指定した
    pub(in crate::my_mod) fn public_function_in_my_mod() {
      print!("called `my_mod::nested::public_function_in_my_mod()`, that\n> ");
      public_function_in_nested();
    }

    // 自モジュール内でのみパブリック、つまりプライベートと同義
    pub(self) fn public_function_in_nested() {
      println!("called `my_mod::nested::public_function_in_nested()`");
    }

    // super=親モジュールからもアクセス可能
    pub(super) fn public_function_in_super_mod() {
      println!("called `my_mod::nested::public_function_in_super_mod()`");
    }
  }
  
  pub fn call_public_function_in_my_mod() {
    print!("called `my_mod::call_public_function_in_my_mod()`, that\n> ");
    nested::public_function_in_my_mod();
    print!("> ");
    nested::public_function_in_super_mod();
  }
}

fn main() {
  my_mod::call_public_function_in_my_mod();
}

// 以下が出力される
// called `my_mod::call_public_function_in_my_mod()`, that
// > called `my_mod::nested::public_function_in_my_mod()`, that
// > called `my_mod::nested::public_function_in_nested()`
// > called `my_mod::nested::public_function_in_super_mod()`
  • ちなみにまだモジュールとクレートの正確な違いは、このテキストでは説明されていない...(次章)。相変わらず説明の順序が適当だ😓

 

[まとめ]

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

空の型、Unitと同じかと思ったら違っててびっくり。

 

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

github.com