メモリとRust所有権
メモリの前提
プログラムを動かすときのメモリは大きく2種類
スタック
char型など文字列リテラルは不変で大きさが決まっているのでコンパイル時にバイナリファイルに直接ハードコードされる
なので読みとり高速
ヒープ
String型など可変はヒープ
なのでヒープを使うと実行は重くなる
通常Stringをもちいてs1を下記のように変数定義すると下の図のようにメモリをOSから取得する
code:rust
let s1 = String::from("hello");
https://scrapbox.io/files/649ff70329b7e9001cfd88f5.png
通常実際の可変なStrignデータ(hello)はヒープに領域を確保し、そのヒープの場所を指し示すポインタを含むs1はスタックに保持される
これを下記のように変数のコピーを行うとどうなるか
code:rust
fn main() {
let s1 = String::from("hello");
let s2 = s1;
println!("{}, world!", s1);
}
同じヒープメモリを指し示すポインタを含むs2スタックメモリがコピーされるようになる
https://scrapbox.io/files/649ff7f8a0b9ad001cc0c361.png
下記のようにヒープをコピーすることはしない
なので実行速度は高速
https://scrapbox.io/files/649ff88911ea28001cb4b04f.png
ただしこれを実行するとエラーになる
https://scrapbox.io/files/649ff98366d91d001be3f411.png
これはmoveということが行われたことを示す
move : つまりs1のスタックをs2にコピーしたと同時にs1は使われなくなったということ。図で示すと下記のようになる
https://scrapbox.io/files/649ff96b9b6872001b0a0c24.png
なのでもしs1のスタックを生き残らせたかったら下記のようにcloneというのをする
code:rust
let s1 = String::from("hello");
let s2 = s1.clone();
println!("s1 = {}, s2 = {}", s1, s2);
ただしその場合は下記のようにヒープ自体をコピーするのでよりメモリを確保する必要があり実行速度は遅くなる
https://scrapbox.io/files/649ff88911ea28001cb4b04f.png
ちなみに下記のように整数のような場合はコンパイル時にサイズが決まっているのでスタックにそのまますっぽりと入れることができるため、エラーが起こらない。(ヒープを使う必要がない)
code:rust
let x = 5;
let y = x;
println!("x = {}, y = {}", x, y);