###### tags: `Rust`
# Rust テキスト学習 2
# 4. シンタックスとセマンティクス 続き
## 所有権(ownership)
Rust所有権システム解説その1
- 所有権 <- 今ここ
- 借用、参照
- ライフタイム
### 概論
Rustが重視するもの = 安全性&スピード
それらを達成するためにたくさんの「ゼロコスト抽象化」を行う
ゼロコスト抽象化 = 抽象化機能を小さいコストで実現すること
所有権システムもゼロコスト抽象化の1つ
解析はすべてコンパイル時に行われ、実行時のコストはかからない
ユーザーの学習曲線というコストはあるが
# 所有権
変数束縛 = リソースの所有権を持つこと
変数のスコープが外れるとき、Rustはその変数に束縛されているリソースを開放する。
```rust=
fn foo() {
let v = vec![1, 2, 3];
}
```
関数が終了して、v が開放されるとき、ヒープに確保されたVec\<T>をも開放する。
vec![T, T, T] = Vec を作るマクロ
末尾に ! がついてるものはマクロの確率が高い
## ムーブセマンティクス
ムーブ = 移動
セマンティクス = そのコードがどう動くか
つまり、代入分で別の変数に移動しようとしたらどうなるか、という意味
```rust=
let v1 = vec![1,2,3];
let v2 = v1;
println!("{} {}", v1[0], v2[0]);
```
これはエラーになる。
`let v2 = v1` : 所有権の移動。ムーブという。
上記の式が意味するものは、 **ヒープ上の[1,2,3]** を参照するVecオブジェクト(ヒープへのポインタ含む)を代入しているということ
ポインタのコピーを作る = 同じ内容へのポインタが2つある
これはデータ競合を引き起こし、安全性保証への違反になるため
Rustはmoveを終えたあとのv1を使用不可にする。
## Copy トレイト
i32, bool, f64 などは、変数束縛を他のものに移動しても
元の変数が利用不可能にならない。
それは、Copyトレイトを実装していてデータのコピーを作成しているから。
Copyトレイとは所有権ルールの挙動を変更することができる。
## 関数の引数の所有権
もちろん、関数の引数として変数を渡した場合もムーブが生じる。
返り値で変数の所有権を返してもらわなければ、VecやBoxなどは使えなくなる。
これを回避して、変数内でCopyトレイトを持たない型を操作するには
借用(Borrow)という機能を使う
# 借用(Borrow)
- 所有権
- 借用、参照 <- 今ここ
- ライフタイム
## 借用を生かさない厄介なコード
```rust=
fn foo(v1: Vec<i32>, v2: Vec<i32>) -> (Vec<i32>, Vec<i32>, i32) {
// v1とv2についての作業を行う
// 所有権と関数の結果を返す
(v1, v2, 42)
}
let v1 = vec![1, 2, 3];
let v2 = vec![1, 2, 3];
let (v1, v2, answer) = foo(v1, v2);
```
もし、変数の戻り値を受けなかったらv1, v2は二度と使えなくなる。
## 借用を活かしたコード
```rust=
fn foo(v1: &Vec<i32>, v2: &Vec<i32>) -> i32 {
// v1とv2についての作業を行う
// 答えを返す
42
}
let v1 = vec![1, 2, 3];
let v2 = vec![1, 2, 3];
let answer = foo(&v1, &v2);
// ここではv1とv2が使える!
```
借用(Borrow)とは要するに、引数に所有権を渡す代わりに
参照& を渡すこと
- & : 参照演算子
- &T: 参照型
参照は、リソースを所有しない。
所有権を借用するだけ。
もし、参照型の変数が定義されたブロックが終了しても、
参照変数が開放されるだけでリソースは開放されない。
また、参照はデフォルトでimutableである。
## &mut 参照
```rust=
let mut x = 5;
{
let y = &mut x;
*y += 1;
}
println!("{}", x);
```
参照したリソースを変更できる。
## 借用のルール
- 借用は所有者よりも長く存続してはならない
- imutableな参照(&T)はいくつでも作れる
- mutableな参照(&mut T)は同時に1つしか存在できない
- &T と &mut T は両立しない
### &T と &mut Tは両立しない
```rust=
let mut x = 5;
let y = &mut x;
*y += 1;
println!("{}", x);
```
y に &mut T で借用されているので、println!() でxの&Tの参照を借すことができない
```rust=
let mut x = 5;
{
let y = &mut x; // -+ &mut借用がここから始まる
*y += 1; // |
} // -+ ... そしてここで終わる
println!("{}", x); // <- ここでxを借用しようとする
```
{} のスコープ内で y = &mut x の期限が切れるのでprintln!()でxの&Tを借用できる。
### メリット: イテレーターの無効化回避
以下のコードは、ループごとに元のVec\<i32>に要素をpushして無限ループにしようとしている。
しかしfor loop によって要素の参照を借用しているので、
元のVecを変更できなくなり、問題を回避する
```rust=
let mut v = vec![1, 2, 3];
for i in &v {
println!("{}", i);
v.push(34);
}
```
### 参照は所有者よりも長く生存してはならない
### メリット: 解放後の使用の制限
```rust=
let y: &i32;
{
let x = 5;
y = &x;
}
println!("{}", y);
```
リソースの x = 5はスコープが切れて開放される。
しかし、yがxより前に定義されている/xが開放された後も生存していることにより、ルールに抵触してエラーが発生する。
これにより、開放後の不正な領域を参照することがなくなる。
```rust=
let y: &i32;
let x = 5;
y = &x;
println!("{}", y);
```
上記の場合もエラー。
yの参照を使っている部分はxの生存期間内だが、xを参照する要理も早くyが宣言されている=xより生存期間が長いので
エラーになる
# ライフタイム(Lifetime)
- 所有権
- 借用、参照
- ライフタイム <- 今ここ
## 複雑なリソース参照の問題
1. リソースを関数に借用させる
2. 関数内でリソースを使い終わり、開放することを決める
3. うっかりして、関数外でまたリソースを使ってしまう
いわゆる、無効なリソースの使用である。
これをダングリングポインタ、または「解放後の使用」という
2.のあとに3.が起こらないように、Rustでは **ライフタイム** を使うことで管理する
ライフタイム = 参照が有効なスコープの記述
## ライフタイム
'a : ライフタイムa と呼ぶ
技術的には、参照はすべてライフタイムを持つ。
しかし、一般的にはコンパイラが省略してくれる。
```rust=
// 一般的には'aをつけない関数と等価
fn foo<'a>(x: &'a i32) {
}
```
### fn func<>(...)
関数は 関数名<> の <>の間に、 **ジェネリックパラメータ** を持つことができる。
ライフタイムはその1種
```rust=
// 1つの参照を借用する場合
fn bar<'a>(...)
// 2つの場合
fn foo<'a, 'b>(...)
// 複数の参照を1つのライフタイムで表すこともできる
fn x_or_y<'a>(x: &'a str, y: &'a str) -> &'a str {}
// mut参照したいとき
fn buzz<'a>(x: &'a mut i32)
```
&mut参照のときは & と mut i32 の間にライフタイムが入る
読み方は、「ライフタイム'aをもつ i32へのmutable な参照&」
### struct name<>{...}
Structもライフタイムを持てる。
構造体がメンバに参照を持つとき、参照元よりも長く生存しないことを
保証する必要があるため。
```rust=
struct Foo<'a> { age: &'a i32 }
```
### impl<'a> Foo<'a> {...}
impl行でもライフタイムを必要とする。
ライフタイム'aをもつimplを、ライフタイム'aをもつFoo構造体が使用するということ
## ライフタイムが有効に働く例
```rust=
struct Foo<'a> {
x: &'a i32,
}
fn main() {
let x; // -+ xがスコープに入る
// |
{ // |
let y = &5; // ---+ yがスコープに入る
let f = Foo { x: y }; // ---+ fがスコープに入る
x = &f.x; // | | ここでエラーが起きる
} // ---+ fとyがスコープから出る
// |
println!("{}", x); // |
} // -+ xがスコープから出る
```
x: 一番寿命が長い。 & &'a i32
y: ブロック内でスコープが尽きる。 & i32
f: ブロック内でスコープが尽きる。 Foo<'a>{ x: &'a i32 }
ライフタイムを定義するということは、 **スコープに名前をつける** ということである。
&'a i32 の参照は、それが借用される中盤の {} の中でのみ使用できるということ
## 'static
static ライフタイムは特別なライフタイム。
プログラム全体を通して使われるので、ライフタイムに終わりがなく、常に参照できる。
'staticライフタイムをよく使うのは、文字列リテラルを扱うとき
```rust=
let x: &'static str = "Hello, world";
```
**文字列リテラルの型** : &'static str
データセグメント(静的領域)に焼き付けられた値であり、参照は常に有効。
## static修飾子
変数をstatic領域に定義する。
この場合も、参照は常に有効。
```rust=
static PI: f64 = 3.14;
let x: &'static i32 = &PI;
```
## ライフタイムの省略
人間にとっての使いやすさのため、ライフタイムは省略できる。
入力ライフタイム = 関数の参照引数のライフタイム
出力ライフタイム = 関数の戻り値のライフタイム
以下の3つの明確なルールによってライフタイムを推論する。
- 入力ライフタイムを省略すると、互いに異なるライフタイムが自動割当
- 入力ライフタイムが1つだけなら、関数の戻り値の省略されたライフタイムに割り当てる
- 入力ライフタイムが複数のとき、&serf か &mut selfがあれば、省略された出力ライフタイムはselfと同じになる。
### 省略例
```rust=
// 'static ライフタイムの省略
let s: &str = "hello";
fn print(s: &str);
```