簡単な Windows GUI アプリを作りながら Rust を学ぶ (2)

投稿: 2021年11月11日
タグ: 
  Developer(Java等)  Windows  技術メモ

前回の続き。5番目から見ていきたい。

5. Rust では Result型を返すように関数実装することがよくあり、Result型のヘルパーメソッドとして expect と unwrap がよく使用される

Rust では関数の戻り値として Result型(std::result::Result)open_in_new がよく使用され、Result は列挙型で「Ok」か「Err」の値を持つ。 Result を直接使用する場合、以下のように実装できる。

Rust
let mut result: Result<i8, &str> = Ok(0); println!("{}", result.is_ok()); // true と表示される result = Err("エラー"); println!("{}", result.is_ok()); // false と表示される

Result<i8, &str><i8, &str> の部分は、Ok、Err それぞれが持つ値の型を指定する。 この例の場合だと、Ok は i8(8ビット Integer)、Err は &str(文字列参照) となる。

Rust はエラーハンドリングを try/catch のよくある例外処理で実装させるのではなく、 正常(Ok) か異常(Err) かを判断させる方法を採っている※。 Wikipedia の ページopen_in_new の言葉を借りると Result型は一般的な例外処理より『

エレガントな方法
』で、Swift や Scala 等でも採用されている模様。
※ Rust にも別途 try-catch を実装する方法(クレート) はある。

元々のお題の Windows GUI アプリ実装(以下) だと、nwg::init() 関数が Result を返す。 正常に init できれば Ok、問題があれば Err が返る。

Rust
nwg::init().expect("Native Windows GUI の Init 失敗");

Ok が返ってくればそのまま後続の処理に進んでいくが、Err が返ってきた場合は Panic が発生する。
Result の expect関数は Panic関数で出力するメッセージを指定できる。 特にメッセージ追加が必要ない場合は、expect の代わりに unwrap関数を使用する。

Rust の Panic は具体的に何をやっているか? - Windows(MSVC) の場合

Rust プログラム内で Panic が発生すると処理が終了し、エラーが出力される。Panic については このドキュメントopen_in_new が参考になるが、実際のコードやプロセスを確認したい。

Panic を起こした Rust プロセスを Windows デバッガでデバッグしたところ、最終的にはソースコードの以下の箇所で C の関数(_CxxThrowException) を実行して処理を終えていた。 (ただ、これは MSVC(Microsoft Visual C++) を利用する場合のパスなので、環境が違えば違うコードが実行される可能性はある)

rust/library/panic_unwind/src/seh.rs - panic
https://github.com/rust-lang/rust/blob/1.56.1/library/panic_unwind/src/seh.rs#L313open_in_new

6. 他の構造体の関数(関連関数) を実行する際は、Rc::new のようにコロン2つで表記する
7. モジュールパスを省略する際は use 宣言を使用する
8. use 宣言でインポートしたものに as をつけることで別名を指定できる

6. 7. 8. は Rust はこういう構文を採用している、ということで覚えるしかない。
全体的に Rust の構文は全体的には C++ に似てると思うが、違う部分もあるので把握していく必要がある。

Rust
use std::rc::Rc; fn main() { Rc::new(""); }

上記のコードは、以下のようにも書けるし、

Rust
fn main() { std::rc::Rc::new(""); }

以下のようにも書ける。

Rust
use std::rc::Rc as Alias; fn main() { Alias::new(""); }

9. match 式でパターンマッチができる
10. match 式の中で _ を指定することでどの条件にも合致しない場合を表現できる

多くのプログラミング言語で switch式として定義されているパターンマッチを Rust では match式open_in_new で定義する。 以下コードの場合、変数 x の値が評価されて、一致する 1 のみがプリントされる。

Rust
fn main() { let x = 1; match x { 1 => println!("1"), 2 => println!("2"), _ => println!("その他") } }

Rust では以下のコードはコンパイルエラーになる。match式のパターンとして一致しない可能性(条件として網羅されていない) があるというエラーになる。 全ての数値や文字列を条件に列挙していくわけにもいかないので、_ (アンダースコア) を使用する。

Rust
fn main() { let x = 1; match x { 1 => println!("1"), 2 => println!("2") } }

Rust では _ は ワイルドカードパターンopen_in_new として定義されており、全ての値とマッチする。
他のプログラミング言語だと見かけない構文なので、初見は「なんだこれ」と思ったが、分かってみると1バイトで表現できて分かりやすい。

  • 5. Rust では Result型を返すように関数実装することがよくあり、Result型のヘルパーメソッドとして expect と unwrap がよく使用される
  • 6. 他の構造体の関数(関連関数) を実行する際は、Rc::new のようにコロン2つで表記する
  • 7. モジュールパスを省略する際は use 宣言を使用する
  • 8. use 宣言でインポートしたものに as をつけることで別名を指定できる
  • 9. match 式でパターンマッチができる
  • 10. match 式の中で _ を指定することでどの条件にも合致しない場合を表現できる