⚙️

Rustの所有権をざっくり言語化する

に公開

所有権、いまだに理解が難しい人へ

Rustというプログラミング言語がありますが、
学習を始めたばかりの人の心を折りがちな要素があります。
それが、所有権という特有の概念・システムです。

画期的で効率的なメモリ管理システムであるが故に、
非常に独自性が強く、正直、イメージがつきづらい部分が大きいでしょう。

そのため、Garbage Collector(ガベージコレクタ)(以下、GC)との違いは何か、
GCと比べて性能にどのような影響があるのか、
それについて、これから書いていきます。

GCに関する解説について

本記事では、GCに関する詳しい説明は省略します。
その理由については、GCについて説明すると
GCを使うメリット・デメリットについても
記述し、閲覧していただくことになり、
執筆者と閲覧者双方に負荷が高くなるためです。

所有権ってなに?

所有権とは何なのか。
唐突ですが、プログラムを実行し、動作させることを、
これから例えて説明します。

プログラムの実行をランニングで考えてみる

ここでは人がランニングすることで例えようと思います。

なぜランニングを選んだかといいますと、
プログラミングにおいて、プログラムを実行することを
走らせると表現することがあるからです。

例えば、Rustのプログラムを実行する時のコマンドの一つに
cargo runがありますが、
これのrunも「走る」という意味の英単語ですね。
ここにメモリの例えを加えましょう。

例える中での定義の説明

例えるにあたり、
実際のものと例えに使う内容の対応表を以下に示します。

実際のもの 例えた先の内容
動いているプログラム ランナー
メモリ カバン
データ(値) 荷物
指示者 各システム(所有権/GC)

また、以下の定義があるとします。

  • ランナーはカバンを背負って走っている
  • ランナーは、特定のタイミングでカバンから荷物を取り出して使う
  • 荷物を詰め込みすぎると、重すぎて走れなくなってしまう
  • 重たすぎて走れなくなった時、荷物を落として転んでしまう
  • 落とした荷物は必ず破損してしまう
  • 走れなくなってしまう状態を オーバーフロー(メモリ不足) と呼称する
  • ランナーに対して、カバンに入っている荷物の増減を管理する指示者がいる
    (所有権やGCの流れに基づいて管理する役割)

所有権ではこれがどうなるの?

所有権を採用しているRustでは、
走り始める時点で、荷物を増やしたり減らしたりするタイミングが書かれている指示書
ランナーに渡されているというイメージができれば、その時点で十分に理解されているはずです。

そのイメージの解釈について

  1. 走り始める時点
  2. 荷物を増やしたり減らしたりするタイミングが書かれている指示書が渡されている

とはどういうことなのかについても説明します。
まず、Rustcargo runなどのコマンドによって
プログラムの実行が始まる前に、プログラムのコンパイルを行うのですが、
コンパイル時に、一定に定められた規則を守っているかどうかのチェックが行われます。
そのチェックが何を果たしているかというと、
荷物が溢れてしまわないかどうかをチェックしています。
(厳密には他の内容もあるのですが、あくまでざっくりなので、このレベルにしています。)
そして、そのチェックが通ったらコンパイルされて実行可能な状態になり、
そのまま実行されます。
これが走り始める時点と表現した理由です。

この時点で、ランナーは荷物が溢れてしまうことがないがわかるとともに、
指示者から、暗黙的にどこで荷物を減らせばいいかについても
既に指示が渡されている状態になっています。
これが荷物を増やしたり減らしたりするタイミングが書かれている指示書
渡されていると表現した理由です。

GCとの違いは?

GCとどう違うかどうかについてですが、

GCでは、

  1. ランナーは一定のタイミングでカバンに入っている荷物の内容を全て確認する
  2. 現状のカバンの中身を指示者に報告する
  3. その報告を受けた指示者の指示に従い、荷物を減らす

という流れになるため、

所有権とは異なり、
逐次的な指示を受ける流れになっているため、
それに伴って、CPUへの負荷が高くなりやすいところがあります。

GCと比べて良いの?

メモリの管理のしやすさと、高速動作の安定性においては非常に優れています。
実際の事例として、Discordというゲームプレイヤー向けの
テキストメッセージで連絡を取り合うSNSのサービスの実装を
GoからRustに書き換えた事例が存在します。
https://discord.com/blog/why-discord-is-switching-from-go-to-rust

この記事において、GCを採用しているGo言語と
所有権を採用しているRustでのプログラム実行の比較項目において、
CPU使用率やレスポンス所要時間の平均値などを比較した結果、

CPU使用率については、GCの実行によって頻繁に急増(38%程度に増加)しては、
もとの使用率(18%〜20%)に戻るような不安定な変動から
所有権のシステムを使用することによって、ある程度安定した
動き(24〜26%程度)に改善されています。

平均レスポンス所要時間は、GCの実行があったタイミングで、
10ms~15msや25~26msに急増したり、
2~3ms程度のレスポンスができている区間があったりと、
これもまた不安定な動作をしていましたが、
所有権システムの使用によって、2~3msの速度で安定してレスポンスが
できるようになっています。

コードは書きやすい?

上記のメリットの代償と言ってはなんですが、
コードの書き方にクセが生まれています。
所有権というシステムについてくるように、
ライフタイムという概念もついてきました。

これは 変数の参照が有効な範囲(スコープ) のことなのですが、
これを明示的に宣言しないといけない場合や、
その範囲が広いものが、それより短い範囲の値を参照しようとすると
コンパイルエラーが発生するようになっているように、様々な制約がついてきます。

そのため、メモリ安全には書きやすいですが、
その代わり記述量や考えることが増えるようになっています。

終わりに

今回は、Rustの所有権についてざっくり説明しました。
より理解をしたい場合は公式ドキュメントを読んでいただいたり
していただくことになりますが、
「所有権でつまづいてしまってわからない」
という人の助けになれば幸いです!

GitHubで編集を提案
Sun* Developers

Discussion