## 所有権とは? - [[スタック]]と[[ヒープ]] - [[スタック]]上のデータサイズはすべて既知の固定サイズでなければいけない - [[ヒープ]]が遅いのは[[ポインタ]]を頼りに目的の領域まで移動(検索?)する必要があるから - 規則 - [[Rust]]の各値は、所有者と呼ばれる変数と対応している - いかなる時も所有者は1つ - 所有者がスコープから外れたら値は破棄される - `push_str`でStringに後ろに文字列リテラルを追加 - [[Rust]]では変数がスコープから抜けるとき [[Dropトレイト]]の`drop`が自動で呼ばれる - ここでメモリの返還をしている Stringの中身について。`ptr`、`len`、`capacity`を確認できる。 ![[Pasted image 20221105212418.png]] 上記 [[VSCode]]で`s2`がデバッガーに出現しないのは、`s1`も`s2`も`s1`が確保した[[ヒープ領域]]を参照しているからなのか? ```rust fn main() { let s1 = String::from("hello"); println!("{}", s1); let s2 = s1.clone(); println!("{}", s2); } ``` としても`s2`は出現しない..。(ただ、[[Rust]]の文法的には[[ディープコピー]]と見なしていいはず) ```rust fn main() { let s1 = String::from("hello"); let s2 = s1.clone(); println!("{}", s1); println!("{}", s2); } ``` これだと最終行で出現する! もしかして、異なる[[ヒープ領域]]を参照させる(確保する)必要がないのであれば、同一の[[ヒープ領域]]を使うから`s2`の実体が現れないのでは...!? 話を戻す。 ```rust fn main() { let s1 = String::from("hello"); let s2 = s1; println!("{}", s2); } ``` `main`関数では`s1`と`s2`が存在し、それぞれスコープから抜ける瞬間がくる。しかし、両者の[[ヒープ領域]]は同一のため、メモリの二重開放エラーとなってしまい、メモリー破壊につながる。 ただ、[[Rust]]は実際にそのようなことはしない。`let s2 = s1`の段階で`s1`は無効と判断され、スコープを抜けてもメモリ解放を行わなくなるから。[[所有権]]が移る処理を[[ムーブ]]といい、『`s1`は`s2`に[[ムーブ]]された』という。 - [[Rust]]では自動的にデータの[[ディープコピー]]が行われることは絶対にない - 整数のようなprimitive型に[[ムーブ]]は発生しないので、束縛も無効化されない - そのようなものは[[Copyトレイト]]が実装されている。 - 具体例 - 整数型 - 論理値型 - 浮動小数点型 - 文字型`char` - [[タプル (Rust)|タプル]] (要素がすべて[[Copyトレイト]]を実装している場合のみ) - 関数の[[実引数]]に[[Copyトレイト]]を実装していない型の変数を渡すと[[ムーブ]]する - 関数呼び出し後、[[実引数]]は使えない - 関数の戻り値でも[[ムーブ]]する 関数の[[実引数]]に指定した変数を、関数呼び出し後も遣いたいことは多いはず。ただ、そのために==仮引数を関数から常にreturnする==のは非常にいただけない。 ## 参照と借用 [[参照]]の例。 ```rust fn main() { let s = String::from("hello"); print(&s); println!("{:?}", s); } fn print(some_thing: &str) { println!("{:?}", some_thing); } ``` この場合、`some_thing`がスコープを抜けても( = print関数を抜けても)、`some_thing`が指すものをdropすることはない。つまり、`print`関数が終了しても、`some_thing`が指す[[ヒープ領域]]、すなわち`main`関数の`s`は無効化されない。 - [[借用]] - [[借用]]した[[仮引数]]に変更を加えたい場合はそれぞれに`mut`の追加が必要 ```diff fn main() { - let s = String::from("hello"); + let mut s = String::from("hello"); - change(&s); + change(&mut s); } - fn change(some_string: &String) { + fn change(some_string: &mut String) { some_string.push_str(", world"); } ``` - [[可変参照#可変参照 の制約]] - [[特定のスコープである特定データに対して1つしか可変参照を持てない]] - [[特定のスコープである特定データに対する不変参照が存在する場合は可変参照を持てない]] - [[ダングリングポインタ]]は仕組み上発生しない 以下はコンパイルエラーになる。 ```rust fn main() { let r = dangle(); // rが指す領域はdangle関数を抜けるときに解放済なので危険!!! } fn dangle() -> &String { let s = String::from("hello"); &s } ``` 関数では[[参照]]ではなく実体を返すようにすればいい。ということは[[Rust]]で[[構造体 (Rust)|構造体]]を生成する関数は実体を返すのが基本になるのかな? ## スライス型 - `&s[0..2]`がスライス - `&str`はスライス = リテラル