Rust でう~ん?と思うところのメモ

expect というメソッド名

Perl の die() に似ている関数で、エラーメッセージを出してプログラムを強制終了(Rustでは panic!()) する Result 型にあるメソッド。

APIリファレンス(↗)をみると、 引数に渡すメッセージがどうあるべきか(Common Message Style ↗)まで 指定されているんだけど、The Book のサンプルコード見てても、それに従ってないような気がする。

expect という関数名も、そのメッセージスタイルに従うことを前提として関数名を決めたみたいなんだけど、 この関数名のせいで、コードが読みにくくなっている気がする。いわゆる、不適切な命名*1にみえてしまう。

まだ Rust になじんでないだけかもしれないけど。

Common Message Style も一応読んで見たけど、まだピンとこない。

  1. エラーが起こる
  2. どんなエラーかをプリントする。

というのは悪いスタイルらしく

  1. エラーが起こる
  2. 何を期待していたか(どうしていればエラーを回避できたか)をプリントする。

というルールがよいとしている。 でも、まず、エラーが起こったことを教えてほしいな。どうすればエラーを回避できたかはその後な気がする。 まあ、panic! するので、何らかのエラーが起こったことは分かってるはずだから、回避方法だけ書けばよい! という意図のようだけど。 (あと、エラーが出たときにプログラマがコードを見て、何を期待していたか書いてあるとわかりやすいという意図もあるらしい)

うーん。慣れれば使いやすいのだろうか。まだ分かってない。 まあ、panic! の出力をそのままユーザーに見せること自体、製品レベルのコードではあり得ないだろうから、そこまで気にしなくてよいのかもしれないけど。

ヒープ多用

ヒープの利用を前提としているところが多いような印象。 まあ、String, Vector, Box なんかは仕方ないにしても、 動的な多相性を dyn を使って実現するときも、ヒープメモリの利用が欠かせないように読めたけど、どうだっけ?

あと、すべてのエラーを表すのにも動的な多相性(dyn)を利用するので、エラー処理を統一したいとか思ってもヒープ必要になりそうな?

日本の組み込み開発では、ヒープはなるべく使わないというところもあるので、そのへん普及の足かせになりそう。

まあ、起動時にヒープを取得してずっと使い続けるような運用なら問題はなさそうだけど。

※ヒープの獲得と解放を繰り返すときの問題点

  • メモリが断片化して、いつか必要な量のメモリが確保できなくなる恐れあり。
  • ヒープ獲得時にはロックが必要なので、リアルタイム性確保のためにその遅延が許されない場合もある。
  • 組み込みでヒープ使うときはアロケートシステムを独自実装することになるだろうから、難易度が高いかも。

組み込み開発でRustを使うなら、ヒープも使う方向に舵を切るべきかも。

Rustを使ったLinuxカーネルドライバの開発では、ヒープも使っているぽい映像(↗)あった。 もともとLinuxはヒープ使うのを、ためらったりしてないのかな。

Rust でグローバル変数にアクセスするときは常に Mutex 必要(unsafe という選択肢もあるけど)

組み込みシステムでグローバル変数排他制御は、

  • 同一タイミングに同じ領域に同時アクセスしないことによる排他

という、運用上の排他が行われていることもある。タイミング管理を人間が行う必要があるけど、 OSが提供するロック機能を使う必要が無いので、遅延がないのが利点。

まあ、ロックする時間はちょびっとなので、これはRustによる排他制御を受け入れた方が 不具合は減りそうだから、そのほうがいいかな。

ただし、Mutex とか Arc とか使うときも、どうやらヒープを使うみたいなのでそっちの方がネックかな。

LinuxカーネルドライバをRustで開発するChatGPTが生成したらしき例Gistみると、 Linux との界面は unsafe にしておいて、C と Rust を切り分けてるみたい。

ドライバ用のデータは、C側に持たせておいて、unsafe な API 呼ぶときに渡してもらうようにしているらしい。 これなら、自分でグローバル変数持たずにすむから Rust で Mutex とる必要もないか。

これを、Rust単体でやろうとすると、結局グローバル変数にアクセスするところはすべて unsafe にしておいて 残りは unsafe でないコードでやる感じかな。(レジスタアクセスは unsafe になるだろうけど)

一部の日本語訳にう~ん?

"トレイト境界" → トレイト制約 のほうが好き

trait bound の日本語訳。

トレイトに対して制約を加える機能なので、トレイト制約がよいと思う。
オライリーの『プログラミングRust 第2版』でもトレイト制約を採用しているし。

トレイト境界という日本語はわかりにくい。

bound という英語には、境界線によって区切られた一定の領域の内側、という、「面」的な意味があるようで、 おそらくその意味で bound を使っているんだろうけど、 境界という日本語から最初に想起されるイメージは、領域を区切る一本の線だと思う。なので、意味の脱落が生じている気がする。 (境界をキョウガイと読む場合は、領域に近い意味もあるようだけど、境界はキョウカイと読むほうが多いだろうし)

"デストラクト" → 分割代入 とかのほうがよさそう。

destructuring の日本語訳。

”デストラクト" より、分割代入とかの方がいいな。 そもそも、destructuring と destruct とは、英語でも意味が異なるようなので、destructuring を "デストラクト" と訳すのは単純に間違っていそうな気がする。

destructuring は

fn main() {
    let arr = [100, 200];
    let [first, second] = arr;
    println!("Hello, world! {first}, {second}");
}

みたいなコードのこと。(上のコードでは、配列の内容を first と second という変数に代入している)

JavaScript では分割代入(↗)
またはそのままデストラクチャリングと訳されている様子。

C++ では構造化束縛(structured bindings)(↗)という。

デストラクトというと、C++プログラマからみると Destructor (クラスの破棄関数)と思っちゃうので困る。
ちなみに、Rustで C++ の Destructor に対応するのは Drop トレイトの drop() メソッド。

*1:不適切な命名:関数などに名前をつけるときは、名は体を表すように、わかりやすい名前をつけるべき、という考え方に反しているという意味です。