2017年6月22日木曜日

The Rust Programming Language 2nd 4日目 Ownership 1

![](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

  1. Rustの変数はそれぞれownerという変数を持っている.
  2. それぞれの変数について,同時に複数のownerは存在できない.
  3. ownerがスコープから出ると,その変数はRustのdrop関数によって削除される.

The String type

コンパイル時にすでに決まっている文字列はstring literalと言ってstackに保存されるが,ユーザーからの入力を受け付けるなどするため,コンパイル時に決まっていない文字列はString型といって,heapに保存される.

Ways Variables and Data Interact: Move

変数がheapであるときはRust特有のmoveという現象によって,他の言語では起こらないエラーが出る.
s1という変数は

という形をしていて,ポインタ,長さ,最大長さのデータがスタックとしてあって,さらにポインタの先に具体的なデータが存在する.ここでs2 = s1としたとき,複製されるのはポインタ,長さ,最大長さのデータだけで,具体的なデータは使いまわされる.

enter image description here
さらに,スコープから出たs1s2を削除しようとして,同じポインタを二度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 件のコメント:

コメントを投稿