北海道苫小牧市出身の初老PGが書くブログ

永遠のプログラマを夢見る、苫小牧市出身のおじさんのちらしの裏

今日は安全なシステムプログラミング言語Rustへの招待の日です

IIJさんでのセミナー を聴講に来ましたので、自分用のメモを残しておきます。

安全なシステムプログラミング言語Rustへの招待 / keenさん

  • 2015 年に 1.0.0 がリリース
    • ここからは安定している
  • 6週間ごとのリリース
  • 言語仕様は2つ
    • Rust2015 と Rust2018
    • 移行ツールはある。両方をコンパイルしてリンクすることも可能
  • 実例
    • firecracker AWS Lambda の内側
    • Magick Pocket: DropBoxのストレージマネージャ
    • Servo: レンダリングエンジン
    • Redox : OS。 大学のプロジェクト
    • TiKV: KVS
  • システムプログラミングは?
    • ランタイムがない (GCがない)
    • メモリは自動管理
    • Cと相互連携
    • 生パケット、組み込み系など
  • メモリ配置
    • Cと違い、詰めようとする
    • #[repr(C)] をつけると、 12 となる
  • イテレータ
    • (2..).filter(...).take(...).for_each(...)
    • 最適化により、速いコードになる
  • トレイト
    • Haskellでいう型クラス
    • C++ の concept らしい
    • 静的ディスパッチ、動的ディスパッチ
    • derive : DebugShow 相当
    • 自分を self で受け取る Python 的なスタイル
    • {:?} で Debug プリントができる
  • 型検査
    • 制約を書いてなければ、メソッドを呼べない
    • C++のテンプレートと違ってコンパイル時に落ちる
  • enum
    • generics, 構造体のように引数も取れる
    • 代数的データ型。木構造をきれいに書ける
  • Ordering
    • 値として定義
    • reverse やメソッドチェーンで簡単に定義できたり
  • 例外はない
    • Result 型を利用する
    • ? で早期 return が可能
    • ? がググりようがないのが初心者には辛い
  • fork
    • nix::unistd::fork の戻り値が、型を見るとわかりやすい
  • forkのデモ
    • alacritty : rust 製のターミナルエミュレータ
    • Cargo.toml に依存を書く
    • cargo run で実行まで一度にやってくれる
  • 安全
    • セキュリティ面。余計なデバグが減る
    • gdbを使わなくてもいい
  • 生産性が高い
    • Cは低級言語。便利な機能があり、低レイヤが扱える、両立
    • async / await は OS がなくても動く
      • システムコールが要らない、という意味。ユーザスレッド。
      • アロケートがなくても動く
      • ステートマシンのコードに変更してくれる
      • 言語仕様としては依存しない
  • 活発なコミュニティ
  • ネットワークドライバを書く実験。Rustが若干遅い
    • 5~6%遅い
    • IPC は Rust の方が断然良い → CPUを使い切れているとも言える
    • 低級な言語でCPUを使い切れないより、高級な言語で使い切りたい
  • 安全
    • NULL Pointer, use after free, 範囲外アクセス, data race は起きない
    • race condition は防げない(デッドロック
      • プログラムのロジックのバグなので
  • 範囲外アクセス
    • vec![1, 2, 3] コンパイル時にチェックしない
    • 実行時例外
    • 固定長配列 [1, 2, 3] に定数でアクセスする場合はコンパイルエラー
  • 所有権
    • 値のライフタイムがある
    • 所有権は移動する。移動後にアクセスするとコンパイルエラー ( move セマンティクス)
    • 関数呼び出しによる所有権の移動
    • 2回データが使えないのは不便
  • 借用
    • &data でデータを貸す。
    • & にはポインタを取るという意味もある
    • 所有するポインタ Box もある
  • 借用は元のデータより長生きできない
  • Rustのデータはイミュータブル
  • C言語では *a = 0; *b = 1; return *a をしても 0 が返るとは限らない
    • a == b のとき
    • Rustでは可変参照は複数作れないので、大丈夫
  • Rust では Pointer Alias の制約が強い
    • 挙動がわかりやすく、コンパイラが最適しやすいというメリット
  • コンパイル時に Read-Write lock がかかっている
  • Nullがほしいときもある
    • find の結果がないとき、など
    • Option
      • i32 など、ポインタじゃない値も nullable にできる
      • 値なのでメソッドが生やせる
    • Option<Pointer> は最適化でポインタになる
  • Copy トレイトでマークすると、 move されない
    • i32 など
  • データには所有者がいる、ポインタは実体がある、nullポインタなどはない
  • アプリケーションの設計に関わる
    • C++のお手本のような設計をするとコンパイラは通りやすい
    • 所有者を意識する。長寿のデータ型を所有者とする
    • データ構造 HashMap などは所有者となる
    • 処理のフローを考えて無駄なコピーを廃する
      • HTTP Request 関連のものは Request に全て持たせるなど
  • get get_mut では所有権が奪えない
    • 所有権を奪うには remove
  • リソースを開放する API がない
    • open はあるが close がない
    • lock はあるが free はない
  • close のエラーは握りつぶされるので注意
    • ファイルディスクリプタに対して close がエラーを吐くと何もできないが (ディスクリプタの再利用があるかも)
    • バッファ付きで読み込み専用ファイルディスクリプタを包むと、書き込めるが、close時に落ちる、などはありうる
      • flush を先にしておくことで回避可能
  • Rustの進歩
    • 参照のライフタイムのチェックを緩めたり
  • 所有権があると書けないデータ構造
    • グラフ
    • opt out する : Rc 複数共有、 RefCell 実行時借用チェックなど
  • RcRefCell はマルチスレッドで使うと落ちてくれる
    • Arc Mutex などを使うと O.K.
    • Rust の Mutex は守る値を明示する
    • Send トレイトが実装されていると、 spawn に渡せる
    • ドキュメントを読まなくてもスレッドセーフか分かる
  • 安全性を捨てる
    • Cの関数を呼ぶ
    • 未初期化のメモリ(ハッシュテーブルなど)
    • unsafe ブロックで囲むとなんでもできる (コンパイルが通るが、セグフォ)
  • unsafe
    • nullポインタを作るのは良い。書き込みはNG
    • 基本は使わない
    • 同頑張っても無理な時 ( Vec など)
    • 高速化(境界外アクセスの無視 get_unchecked
    • C の FFI : safe な API でラップするのが普通
  • コミュニティが活発なものは成長する
    • stack overflowの人気一
    • MicroSoft
    • crates.io 30,000+ クレート ( Ruby は 15 万 )
    • コミュニティ中心でオープン / Mozillaがサポートしているだけ。AWSもサポート
  • Linuxユーザが多い(シェアは 1% のはずだが)
    • rust2020 タグを付けると、希望を吸い上げてくれる

  • UB(Undefined Behavior) が起きない
  • 日本語コミュニティもある (rust-jp slack)
    • beginer チャネルもある

  • Q). Sparkでも使える?
    • A). jarからJNIを使って呼び出さないと。厳しい。Sparkを使ったほうがいい(rustで書き直した人も。CPUバウンドを解決できるらしい)