私的Rust学習メモ1(メモリ管理とか)


Rust

なんとなくやりたくなったので学習メモ。

Rustのメモリ管理

CやC++のような手動管理でもなく、最近の言語にメジャーなGCでもなく、所有権ベースのメモリ管理。

他の手法との比較

  • 手動管理の場合は、人為的ミスでメモリ管理に失敗する
  • ガーベージコレクタの場合、実際にメモリが解放可能になってからGCが走るまでのタイムラグがあることによりパフォーマンスに影響が出る場合がある。

Discordの例
個人的な経験としてはJavaでも似たようなメモリ使用量のブレに悩まされたことはある。

Rustのアプローチ

  • Heap上のメモリの所有権を持つ変数を一つに限定し、その変数がスコープを抜けたら解放する GC方式と異なり、不要になった段階で即座にメモリを解放できる。

なぜ一つに限定する?

  • ヒープに乗っているものに紐づく変数が複数あると解放してよいかの判断が難しくなる(GCが頑張っているところ)
  • CやC++のような手動管理は熟練者でもバグを避けられない。
  • 紐づいている変数が一つだけならチェックがコンパイラレベルで可能+二重解放によるバグのリスクもない。

メモリ上の置き場

どこに置かれるかによって、所有権問題を考えなければいけないかが変わる。
* バイナリ
* Stack
* Heap

バイナリ

stringリテラルで作成した文字列などのimmutableな定数。
コンパイル時に確定するので実行ファイル上に存在し、
実行ファイルのバイナリがメモリ上に読み込まれたときにそこへの参照を持つ。
実行中に解放する必要がないので、所有権問題は起こらない。

Stackに置かるもの

数値型やboolean及び、Heapに置かれないものの配列やTuple(及びCopy Traitを持つもの。このあたりは追々)。
基本的に、変数定義時点で必要なメモリ上の領域が確定されるもの。
スコープ内にある時はStackに置かれ、値がコピー渡しされる。(変数への代入や引数として渡すとき)
常にコピー渡しなので、所有権について考える必要なし。

Heapにおかれるもの

上記の二つ以外。
Heapに実体が置かれて、Stackには参照のみが格納されるもの。
一口に文字列といっても、String型は可変長なのでこちら。
メモリ上の使用領域がRuntimeに変わるため、所有権を1変数に限定する必要あり。

所有権の変わるタイミング

別変数への代入


let hoge = String::from("hoge");
let fuga = hoge;

上記のようにすると、fugaの宣言以降hogeは使えない。(所有権がfugaに移る)

引数として渡す


fn hoge_func(hoge:String){//Rustはスネークケースが原則
 println!("hoge is :{}",hoge);
}

///
let hoge = String.from("hoge");
hoge_func(hoge);
//以降hogeは使えない

こちらもhogeFunc内の引数に所有権が渡ってしまうのでhogeFuncの呼び出し以降は使えない。
なお戻り値として返すことで、呼び出し元に所有権を返すことができる


fn hoge_func(hoge:String) -> String{
 println!("hoge is :{}",hoge);
 hoge //;をつけないとその値を返す式扱いとなる(;をつけると文になり戻り値は{}になる。)。早期returnしない場合最後に参照した式の値を返す
}

///
let hoge = String.from("hoge");
let hoge2 = hoge_func(hoge);
//所有権はhoge -> hoge_func内hoge -> hoge2と移る

借用

所有権を渡したくない場合は借用(borrow)を使う。
これは、ヒープに対する参照に対する参照を作って渡す形。

fn hoge_func(hoge:&String){
    println!("hoge is :{}",hoge);
}

let hoge = String::from("aa");
hoge_func(&hoge);

println!("hoge is:{}",hoge);//貸しているだけで所有権を失っていない。

借用とmutation

借用元がmutなら一つだけmutな借用が可能

fn hoge_func(hoge:&mut String){
    hoge.push_str("fuga");
}

let mut hoge = String::from("hoge");
hoge_func(&mut hoge);

println!("hoge is:{}",hoge);//hogefuga

mutの借用は同時に一つしかできない。

fn hoge_func(hoge:&mut String){
    hoge.push_str("fuga");
}

let mut hoge = String::from("hoge");
let h = &hoge;
hoge_func(&mut hoge);//既に借用をしているとダメ。

println!("hoge is:{}",hoge);//hogefuga
fn hoge_func(hoge:&mut String){
    hoge.push_str("fuga");
}

let mut hoge = String::from("hoge");
{
 let h = &hoge;
}
hoge_func(&mut hoge);//これは借用しているhのスコープが切れているのでOK

println!("hoge is:{}",hoge);
fn hoge_func(hoge:&mut String){
    hoge.push_str("fuga");
}

let mut hoge = String::from("hoge");
hoge_func(&mut hoge);
hoge_func(&mut hoge);//ここも一つ目の借用のスコープが切れているのでOK

println!("hoge is:{}",hoge);//hogefugafuga