| Listing 4-1: 変数と、それがvalidなscope |
{ // s はvalid(有効)ではない
let s = "hello"; // s は valid
// s を使う // s は valid
} // scope を抜けたので sはもはや valid ではない。
o
|
s はscopeに入ると valid (有効)になる。s は valid なまま既に3章で紹介したデータ型はどれも既知のサイズであり、スタックの上に保存され、 scopeを抜けると捨てられるものであった。 ownership を理解するにはもう少し複雑なデータ型が必要になる。 異なるスコープで同じ値を使う必要がある場合は、新しい独立したインスタンスがコピーによって生成される。
しかしここでは、ヒープに保存されるデータを扱って、Rust がいかにしてその値が消去される時を知るかを見ていく。 String型がよい例となることだろう。
<今まで見てきた String literal は immutable (変更付加) である。
ユーザ入力を保存するのには String 型がある。 String は mutable (変更可)できる。 String 型は heap にデータを割り当てる。 String 型のtextの量はコンパイル時には不明である。
| String型変数の例 |
let mut s = String::from("hello");
s.push_str(", world"); // push_str() はStringにリテラルを追加する
println!("{s}""); // this will begin
|
String literal の場合は、コンパイル時にサイズが確定しているのでテクストが最終の実行可能ファイルに 直接ハードコーディングされる。これがString literal が高速で効率的な理由である。
String型の場合は、mutable (可変)で増加可能なテキストをサポートするために、 コンパイル時には未知の量のメモリをヒープに確保する必要がある。すなわち
String::from を呼び出せば実現できる。
2番目の項目は異なる。ガベージコレクタを備えた言語では、使われなくなったメモリはガベージコレクタが処理するので、 プログラマが考慮する必要はない。 ガベージコレクタを持たない言語では、使われなくなったメモリを開放するのはプログラマの責任となる。 これを適切に行うことはプログラマにとって大変難しい仕事である。
Rust では異なる道を進んでいる。メモリを所有している変数がそのスコープを抜けたらメモリは自動的に返還される
(drop関数による)。
スコープの例である Listing 4-1 を、 String literal の代わりに Srring を使って記述した版は次のようになる。
| String型変数とスコープの例 |
{
let s = String::from("hello"); // s is valid from th e point forward
// s を使う // s is valid
} // s is no longer valid
|
ヒープ上に確保されたデータを複数の変数が参照していると複雑な場面が起こり得る。 そのような場面について以下で検討していく。
Variables and Data Interacting with Move (moveによる変数とデータの相互作用)
| Listing 4-2: 変数x,yに整数値を代入する |
let x = 5;
let y = x;
|
整数は固定のサイズを持つ単純な値なので、2つの'5'という整数値はスタック上に確保されている。
| Listing 4-2b: 変数s1, s2 に文字列を代入する |
let s1 = String::from("hello");
let s2 = s1;
|
変数 s1, s2 の値は5個の文字からなる文字列であり、これはヒープ上に取られて共有されている。 変数 s1, s2 が scope を抜けたらそれぞれがデータを drop すると、2重開放エラーとなってしまう。
メモリ安全性を確保するために、let s2=s1; 行の後は s1 はもはや valid (有効)ではないと考える。
そのため s1 のscope外に移動したときには何も開放されない。
| Listing 4-2c:所有権が移動するプログラム |
fn main() {
let s1 = String::from("hello");
let s2 = s1;
println!("{s1}, world");
}
|
| Listing 4-2c:メモリ安全性によるエラー |
$ rustc listing4_2c.rs
error[E0382]: borrow of moved value: `s1`
--> listing4_2c.rs:5:16
|
2 | let s1 = String::from("hello");
| -- move occurs because `s1` has type `String`, which does not implement the `Copy` trait
3 | let s2 = s1;
| -- value moved here
4 |
5 | println!("{s1}, world");
| ^^ value borrowed here after move
|
= note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider cloning the value if the performance cost is acceptable
|
3 | let s2 = s1.clone();
| ++++++++
warning: unused variable: `s2`
--> listing4_2c.rs:3:9
|
3 | let s2 = s1;
| ^^ help: if this is intentional, prefix it with an underscore: `_s2`
|
= note: `#[warn(unused_variables)]` (part of `#[warn(unused)]`) on by default
error: aborting due to 1 previous error; 1 warning emitted
For more information about this error, try `rustc --explain E0382`.
|
Scope and Assignment