はじめて触った言語はFortranで、先人が書いたコードをスパコンで動かすというようなことをしたことがある
そのあとCとC++も同じような感じでちょっとだけ触ったことがあるが、どちらもメモリを正しく管理することが難しいというか、あまり理解できぬままにJavaなどを触るようになった
それゆえメモリを意識して書く言語にはちょっと抵抗があったが、Rustを調べていくとCやC++同等の性能を持つが、CやC++よりもメモリ管理が安全で、並列処理もお得意な言語ということでちょっと興味がわいたので本を読んでみた
Rustの特徴
Rustの特徴的な機構として以下があるとのこと
- 所有権(ownership)
- 移動(move)
- 借用(borrow)
これらはC、C++などで煩わしかったポインター利用のミスなどを起こさないように設計された新しいメモリ管理機構で、Javaなどではポインター利用のミスなどを起こさないようする作業をGCがやってくれていたが、そのGCも不要になるのがRustの評判の良いところっぽい
所有権と移動
本の所有権の章だけまとめてみた
本では所有権を理解するのに他の言語との違いを説明していた
Python
次のようなコードをPythonではどうメモリ上で表現されるか
s = ['udon', 'ramen', 'soba'] t = s u = s
sに代入した後のプログラムの状態と、プログラムの実行が終了した時点での状態の図は以下のようになる
Pyhonでは、すべてのPythonオブジェクトが現在そのオブジェクトを参照している数を数える参照カウントをもっているので、 代入した後は参照先のオブジェクトの参照カウントが増えるだけの代入操作になる
代入操作が安価というメリットがあるが、参照カウントの管理が複雑になるデメリットがある
C++
次のようなコードをC++ではどうメモリ上で表現されるか
using namespace std; vector<string> s = { "udon", "ramen", "soba"}; vector<string> t = s; vector<string> u = s;
sに代入した後のプログラムの状態と、プログラムの実行が終了した時点での状態の図は以下のようになる
C++ではベクタを代入していくとベクタのコピーがつくられ、結果定に3つのベクタと9つの文字列が確保された状態になる
オブジェクトの参照元(所有者)が明確なのでメモリをいつ解放して良いかプログラムが決定しやすくなるメリットがあるが、無制限にメモリと計算時間を消費するデメリットがある
Rust
次のようなコードをRustではどうメモリ上で表現されるか
let s = vec!["udon".to_strin(), "ramen".to_string(), "soba".to_string()]; let t = s; let u = s;
一見問題なさそうだが、そもそもこれはコンパイルエラーになる
sに代入した後のプログラムの状態と、次のtに代入した後のプログラムの状態の図は以下
RustはC++と同様なツリー構造になっているが、値を代入すると代入元から代入先へ「所有権」が「移動」する
そして、もともと所有していた親は未初期化状態になるので、未初期化状態の値を使おうとしてエラーになる
このように、Rustはほとんどの型がオブジェクトの単一所有のルールをもつので、参照元のカウント管理をする必要もないし、コピーをつくる必要もないというのが他の言語にない特徴になっている模様
ちなみに、Pythonのように所有権を共有することもできるし(Rc
とArc
というのを使う)、C++のように子のコピーをもつようにすることもできる(clone
メソッドを使う)
まだまだわからないことはあるが、なんとなくメモリ管理が面白い言語だということがわかった