Rustの所有権システム
完全に理解した
@kfurumiya
GitHub
本名
古川 亘
ハンドルネーム
古都こと
しゅみ
ゲーム
プログラミング
好きな言語
TypeScript
Rust
React / React Native
Next.js
AWS / GCP
ウェブパフォーマンス
1991年生まれ
近況
原神(PC版)に
ハマっている
今日のお話
Rustの所有権システム
完全に理解した
本日の献立
- Rust is 何
- 値の所有権
- 借用
- ライフタイム
Rust is 何
Rustとは
use std::process::exit;
use std::env;
fn main() {
let cli_args: Vec<String> = env::args().collect();
let name = match cli_args.get(1) {
Some(a) => a,
None => exit(0)
};
println!("Hello, {}!", name);
}
Rustとは
- 2010年頃に登場
- コンパイル型のモダンなシステムプログラミング言語
- Mozilla従業員の個人プロジェクトから始まった
- チェックが非常に厳格でとても安全にコードを書ける
- ゼロランタイムなため極めて高速に動作する
- 採用例:Firefox、Discord、Dropbox、など
Rustの特徴
全ての値に対する
「所有権」が存在する
Rustの特徴
😗
値の所有権
所有権
(ownership)
値の所有権(ownership)
fn main() {
let val = String::from("Hello");
let val2 = val;
println!("{} {}", val, val2);
}
error[E0382]: borrow of moved value: `val`
--> src/main.rs:5:23
|
2 | let val = String::from("Hello");
| --- move occurs because `val` has type `String`, which does not implement the `Copy` trait
3 | let val2 = val;
| --- value moved here
4 |
5 | println!("{} {}", val, val2);
| ^^^ value borrowed here after move
↓コンパイルエラー
値の所有権(ownership)
fn main() {
// ここで値がvalに束縛される
let val = String::from("Hello");
// val2に所有権がムーブし、valは所有権を失う
let val2 = val;
// valは既に所有権を失っているので
// ここでアクセスするとエラー
println!("{} {}", val, val2);
}
Why???
What's?
所有権とムーブセマンティクス
- 値の使用者をコンパイル時に完全に決定したい
- メモリが安全に解放されることが保証できる
- メモリ安全でないコードは全てエラー
- これを実現するための所有権システム
- RustにGCは存在しないがメモリの明示的解放は不要
- allocした値を自動的にfreeしてくれる
- 値の有効範囲がコンパイル時に確定できる
所有権とムーブセマンティクス
- ムーブセマンティクス
- 値をコピーするのではなくムーブする挙動
- Rustではこれがデフォルト
- C++ではunique_ptrとmoveで実現可能
- ⇆ コピーセマンティクス
- 一般的な言語はこっち
- Rustでも一部のプリミティブはコピーする
- Rustではtrait(mix-inみたいなの)で実現可能
所有権とムーブセマンティクス
fn main() {
let val = String::from("Hello");
{
let _list = vec![val]; // valがVectorにmoveする
} // ここで_listもvalもfreeされてしまう
println!("{}", val); // もう戻ってこない
}
所有者のスコープを抜けると
freeされてしまう
借用
(borrowing)
ムーブの問題
fn string_len(text: String) -> usize {
// returnしているのがusizeなので
// 受け取ったString自体は元の場所にムーブしない……
text.len()
}
fn main() {
let val = String::from("Hello");
// ムーブしたStringが戻ってこない
let len = string_len(val);
// エラー!元のStringはfreeされてしまった……
println!("{} {}", val, len);
}
借用(borrowing)
- 所有権をムーブせず借りることができる
- 借用(borrowing)
- 借用は参照(ポインタ)の形で行われる
- ポインタで渡せば自動的に借用になる
- ムーブしないので返す必要もない
- ただし所有はしていないので制限は大きい
借用してみる
fn string_len(text: &String) -> usize {
// Stringを借用しているだけなので
// 返す必要はない
text.len()
}
fn main() {
let val = String::from("Hello");
// Stringは貸してるだけ
// 所有権はvalの元にある
let len = string_len(&val);
// 動く!!!!
println!("{} {}", val, len);
}
可変参照の借用
fn string_len(text: &String) -> usize {
// エラー!借用しているだけなので
// mutateできない…
text.push_str(" World!");
text.len()
}
fn string_len_2(text: &mut String) -> usize {
// &mut で明示的に可変な借用をする
text.push_str(" World!");
text.len()
}
fn main() {
// 可変な値として束縛する
let mut val = String::from("Hello");
let len = string_len(&val); // エラー!
let len2 = string_len_2(&mut val); // 可変参照を生成する
println!("{} {}", val, len2);
}
ぶら下がりポインタ
fn dangle() -> &String {
let s = String::from("Hello");
return &s;
} // sはここでfreeされる
// よって&sは危険!エラー!
fn main() {
let val = dangle();
}
ライフタイム
(lifetime)
借用のライフタイム
fn main() {
let val = String::from("Hello");
// xのライフタイム 'a
let x = &val;
{
let y = &val; // yのライフタイム 'b
} // 'b ここまで
}// 'a ここまで
'a 'b 'c…と自動的に振られていく!
スコープに名前付けてるイメージ
ライフタイムが確定しない場合
// エラー
// xのライフタイム'aとyのライフタイム'bが自動的に振られるが
// どちらが返るかわからない → 戻り値のライフタイムも確定しない
fn longer(x: &String, y: &String) -> &String {
if x.len() > y.len() {
x
} else {
y
}
}
fn main() {
let a = String::from("aa");
let b = String::from("aaa");
let result = longer(&a, &b);
println!("{}", result)
}
ライフタイムを明示してあげる
// ライフタイムを明示する
fn longer<'a>(x: &'a String, y: &'a String) -> &'a String {
if x.len() > y.len() {
x
} else {
y
}
}
fn main() {
let a = String::from("aa");
let b = String::from("aaa");
let result = longer(&a, &b);
println!("{}", result)
}
まとめ!!
💯 Rustの所有権システム完全に理解した 💯
- Rustでは値の所有権がある
- 使用範囲をコンパイル時検出して自動でfreeする
- 所有権をムーブしたくないときはポインタで借用する
- 関数の引数とか
- 借用にはライフタイムがある
- コンパイラが自動検出できない時は明示する
Rustの所有権システム完全に理解した
By Koto Furumiya
Rustの所有権システム完全に理解した
Rustの所有権システムについて、軽くお話しします。 「【オンライン】エンジニア達の「〇〇完全に理解した」Talk #11 ( https://easy2.connpass.com/event/194418/ )」登壇用資料。
- 2,525