![](https://doc.rust-lang.org/book/second-edition/]
Apache License Version 2.0
Understanding Owenership
ownership(所有権)はRustの最大の特徴で,メモリを安全に扱うことを可能にしている.
この章ではサンプルコードに rust fn main()
はいちいち書かないことにする.
4.1 What is Ownership?
The Stack and the Heap
stackとheapはプログラムから見た仮想メモリの構造.あるいはOSから見た物理メモリの管理方法.
stackはデータを読み込んだ順に積み上げ(push),その逆順に読み込まれ,読み込まれた値を削除する(pop).
stackから値を取り出すとき,取り出す値をいちいち検索しなくて良い(検索できない)うえstackのデータはすべて同じビット長なので,stackは高速.プログラムが関数を呼ぶとき,その引数とローカル変数はstackにpushされて,関数が実行し終わるとそれらはpopされる.
一方,コンパイル時にはbit長がわからなないデータや,bit長が変わるデータのときは,heapを使う.heapにデータを入れようとすると,OSは空いているメモリの一部をheapのために割り当てて,そのメモリの番地(ポインタ)をプログラムに渡す.これをallocationという.stackにpushすることはallocationとは呼ばない.heapへのアクセスは,ポインタを経由しなければならないので,stackよりも遅い.heapを使うときは,heap上のデータの複製を最小限にし,使わなくなったデータは削除して,メモリ空間を開けなければならない.
Ownership Rules
- Rustの変数はそれぞれownerという変数を持っている.
- それぞれの変数について,同時に複数のownerは存在できない.
- ownerがスコープから出ると,その変数はRustのdrop関数によって削除される.
The String type
コンパイル時にすでに決まっている文字列はstring literalと言ってstackに保存されるが,ユーザーからの入力を受け付けるなどするため,コンパイル時に決まっていない文字列はString型といって,heapに保存される.
Ways Variables and Data Interact: Move
変数がheapであるときはRust特有のmoveという現象によって,他の言語では起こらないエラーが出る.
s1という変数は
という形をしていて,ポインタ,長さ,最大長さのデータがスタックとしてあって,さらにポインタの先に具体的なデータが存在する.ここでs2 = s1
としたとき,複製されるのはポインタ,長さ,最大長さのデータだけで,具体的なデータは使いまわされる.
さらに,スコープから出たs1
とs2
を削除しようとして,同じポインタを二度dropしようとすると,衝突が起こって脆弱性につながる.そこでRustはs2=s1
を発行した時点でs1
は無効な変数であると考え,s1
のスタックを削除する.
これは同時に,Rustは自動的には”深い”コピーを作らないということでもある.
fn main() {
let s1 = String::from("hello"); // Stringはheap
let s2 = s1;
println!("s1 = {}", s1);
println!("s2 = {}", s2);
}
shell
error[E0382]: use of moved value: `s1`
--> src/main.rs:5:23
|
3 | let s2 = s1;
| -- value moved here
4 |
5 | println!("s1 = {}", s1);
| ^^ value used here after move
|
= note: move occurs because `s1` has type `std::string::String`, which does not implement the `Copy` trait
```
#### Ways Variables and Data Interact: Clone
深いコピーを作りたいときには```clone()```を使う.
*src/main.rs*
```rust
fn main() {
let s1 = String::from("hello"); // Stringはheap
let mut s2 = s1.clone(); // hardcopy
s2.push_str("_world!");
println!("s1 = {}", s1);
println!("s2 = {}", s2);
}
<div class="se-preview-section-delimiter"></div>
shell
Finished dev [unoptimized + debuginfo] target(s) in 0.35 secs
Running `target/debug/ownership`
s1 = hello
s2 = hello_world!
<div class="se-preview-section-delimiter"></div>
Stack-Only Data: Copy
integerやfloatのような,heapを使わず,stackだけに保存されるデータ型では,常に深いコピーが行われる.
src/main.rs
fn main() {
let x = 5;
let y = x;
println!("the value of x is: {}", x);
println!("the value of y is: {}", y);
}
<div class="se-preview-section-delimiter"></div>
bash
Finished dev [unoptimized + debuginfo] target(s) in 0.29 secs
Running `target/debug/ownership`
the value of x is: 5
the value of y is: 5
<div class="se-preview-section-delimiter"></div>
Ownership and Functions
関数の引数に変数を与えるときも,moveの特性に注意しなければならない.
src/main.rs
fn main() {
let s = String::from("hello");
takes_ownership(s); // この時点でsは失効
let x = 5;
makes_copy(x);
println!("{}", s); // ここでsは参照できない.
}
fn takes_ownership(some_string: String) {
println!("{}", some_string);
}
fn makes_copy(some_integer: i32) {
println!("{}", some_integer);
}
<div class="se-preview-section-delimiter"></div>
shell
error[E0382]: use of moved value: `s`
--> src/main.rs:7:18
|
3 | takes_ownership(s);
| - value moved here
...
7 | println!("{}", s);
| ^ value used here after move
|
= note: move occurs because `s` has type `std::string::String`, which does not implement the `Copy` trait
error: aborting due to previous error
error: Could not compile `ownership`.
To learn more, run the command again with --verbose.
0 件のコメント:
コメントを投稿