Cと少し違うところがある感じがしたのでメモ。
前提として、Cでのポインタ、参照などを分かっていないと難しい。
所有権について
rustには所有権という概念がある。
String型など、ヒープによって確保されるデータ(コンパイル時にサイズが不明なデータとも言える)を変数に格納するとき、そのデータ ”そのもの” ではなく ”データのポインタ” が格納される。(Cでの文字列と同じ感じ。)
もし、下コードのように x
がString型の変数であり、そこから y = x
とするとき、x, y
どちらも、同じ"データのポインタ"が格納される。
そして、rustは__スコープを抜けたら、メモリを開放する__という特徴がある。
もしx, y
で同じポインタを持っているのであれば、同じメモリを解放しようとしてしまう。
これを防ぐためのものが__”所有権”である。
y = x
としたときに___所有権がyに移る_、つまり__xは無効にされる__のである。
なので下コードはコンパイルが通らない。
fn mov()
{
// 前提 rustはスコープから抜けたらメモリを開放する
let x = String::from("hello");
let y = x;
// この時、”所有権”がyに渡される
// 渡されたらxは無効となる(開放するとき、xが有効だとx, yで同じアドレスを開放することになってしまう)
// 所有権はポインタとは違う(参照ではない)
println!("{}, {}", x, y);
// この場合コンパイルエラーが発生(xを指定したため)
}
ちなみに__ヒープで確保されないデータ__(スタックで確保されるデータ、整数型など。)では、値がコピーされるので、下のコードのように
fn cpy()
{
let x = 5;
let y = x;
println!("{}, {}", x, y);
//出力結果:5, 5
}
とすることができる。
所有権はそのままで、データを参照する
参照を使えば、所有権は移動しないで、値を見ることができる。
fn mov()
{
let x = String::from("hello");
let y = &x;
// yはxを参照する 所有権は移動しない
println!("{}, {}", x, y);
// 出力結果:hello, hello
// この場合コンパイルエラーは発生しない
}
ここでは、x
は__文字列データのポインタ__を持っており、y
は__xへのポインタ__を持っている
つまり、二重開放にならず、エラーを吐かない。
ただし、ここでは参照なので、x
の値が変化したらy
も変化してしまう。
借用されているのでそもそもxの値を変化できないです(&mutとか使えば違うのかもしれないですけど)。2か月前の自分変なこと書いてる...
それが嫌なら、__depp copy__をすればいい。これでヒープデータが完璧にコピーされる。
fn mov()
{
let x = String::from("hello");
let y = x.clone();
// yはxのdeep copyとなる つまりhelloという文字列がメモリ空間に二つ確保される
println!("{}, {}", x, y);
// 出力結果:hello, hello
}
まとめ
ヒープで確保されるデータ ... 一つのデータで、アドレスを参照することによってやりくり -> ムーブ
rustはこのようなデータはデフォルトでは一つでやりくりする?
スタックで確保されるデータ ... データを複製できる -> コピー
__所有権が移る = データがムーブされる__といった感じでいいのだろうか。
#参考
https://doc.rust-jp.rs/book-ja/ch04-01-what-is-ownership.html