Rust
2015 Rust 1.0 リリース
Rust自体は2010年リリース
同時期にC++に起こったことを見るのが良い
2011年のC++11
std::unique_ptr
確保したメモリ領域を指すポインタがユニークであることが保証できるなら、スコープを抜ける時に解放して良い
C++は過去に書かれたソースコードとの互換性を保つことを重視している
なので「unique_ptrで統一した方がいいのになぁ」と思っても、そうやるわけにはいかない
そこでRustが生まれた
GC以前、参照カウント、マーク&スイープ、あたりから書く?
参照カウントとshared_ptr
RAII・参照・借用チェッカー?
話をする上でRAIIなしでは難しい気がするな
C++11 ムーブセマンティクス
右辺値参照の話になると大部分の読者には行き過ぎかな
C++のメモリ管理と捉えると他の言語の人が興味を失う
「開けたら閉めろ」的な制約に関する
「電気をつけたら消せ」
「でもまだ使ってる人がいるなら消してはいけない」
所有権を理解する
所有権はRustの最もユニークな機能であり、これのおかげでガベージコレクタなしで安全性担保を行うことができるのです。 故に、Rustにおいて、所有権がどう動作するのかを理解するのは重要です。この章では、所有権以外にも、関連する機能を いくつか話していきます: 借用、スライス、そして、コンパイラがデータをメモリにどう配置するかです。
(メモ: 余計なスコープを後で消す)
code:rust
fn main() {
{
let x = 1;
let y = x;
println!("x: {}", x);
println!("y: {}", y);
}
}
code:output
x: 1
y: 1
code:rust
fn main() {
{
let x = String::from("hello");
let y = x;
println!("x: {}", x); // NG
println!("y: {}", y);
}
}
code:error
errorE0382: borrow of moved value: x --> src/main.rs:5:27
|
3 | let x = String::from("hello");
| - move occurs because x has type String, which does not implement the Copy trait
4 | let y = x;
| - value moved here
5 | println!("x: {}", x);
| ^ 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
|
4 | let y = x.clone();
| ++++++++
C++だと
code:cpp
int main () {
std::string x("abc");
std::string y;
y = x; // (*) copy
std::cout << x << std::endl; // OK
std::cout << y << std::endl;
}
code:cpp
int main () {
std::unique_ptr<std::string> x = std::make_unique<std::string>(std::string("abc"));
std::unique_ptr<std::string> y;
std::cout << *x << std::endl;
}
no member named 'make_unique' in namespace 'std'
make_unique is an upcoming C++14 feature
こうか
$ clang++ -std=c++14 t.cpp && ./a.out
code:cpp
int main () {
std::unique_ptr<std::string> x = std::make_unique<std::string>(std::string("abc"));
std::unique_ptr<std::string> y;
y = std::move(x);
std::cout << *y << std::endl;
std::cout << *x << std::endl; // here
}
code:output
abc
Segmentation fault
他の言語を触っている間に"shallow copy"と"deep copy"という用語を耳にしたことがあるなら、 データのコピーなしにポインタと長さ、許容量をコピーするという概念は、shallow copyのように思えるかもしれません。 ですが、コンパイラは最初の変数をも無効化するので、shallow copyと呼ばれる代わりに、 ムーブとして知られているわけです。
Rustでは、 自動的にデータの"deep copy"が行われることは絶対にないわけです。
値がスタックに置かれてるかヒープに置かれてるかを意識する必要がある
わかってる人にとってはさほど難しいことではない
今まで意識していなかった人にとっては、新しく意識することが増えるわけだ
code:rust
fn main() {
{
let x = String::from("hello");
foo(x);
println!("x: {}", x); // NG
}
}
fn foo(x: String){
println!("x: {}", x);
}
code:error
errorE0382: borrow of moved value: x --> src/main.rs:5:27
|
3 | let x = String::from("hello");
| - move occurs because x has type String, which does not implement the Copy trait
4 | foo(x);
| - value moved here
5 | println!("x: {}", x); // NG
| ^ value borrowed here after move
|
note: consider changing this parameter type in function foo to borrow instead if owning the value isn't necessary
--> src/main.rs:8:11
|
8 | fn foo(x: String){
| --- ^^^^^^ this parameter takes ownership of the value
| |
| in this function
= 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
|
4 | foo(x.clone());
| ++++++++
所有権を取り、またその所有権を戻す、ということを全ての関数でしていたら、ちょっとめんどくさいですね。 関数に値は使わせるものの所有権を取らないようにさせるにはどうするべきでしょうか。
code:rust
fn main() {
{
let x = String::from("hello");
foo(&x);
println!("x: {}", x); // OK
}
}
fn foo(x: &String){
println!("x: {}", x);
}
s1の値を参照する参照を生成することができますが、これを所有することはありません。 所有してないということは、指している値は、参照がスコープを抜けてもドロップされないということです。
cannot borrow s as mutable more than once at a time
これは、新たなRustaceanにとっては、 壁です。なぜなら、多くの言語では、いつでも好きな時に可変化できるからです。...この制約がある利点は、コンパイラがコンパイル時にデータ競合を防ぐことができる点です。 データ競合とは、競合条件と類似していて、これら3つの振る舞いが起きる時に発生します:
2つ以上のポインタが同じデータに同時にアクセスする。
少なくとも一つのポインタがデータに書き込みを行っている。
データへのアクセスを同期する機構が使用されていない。
データ競合は未定義の振る舞いを引き起こし、実行時に追いかけようとした時に特定し解決するのが難しい問題です。 しかし、Rustは、データ競合が起こるコードをコンパイルさえしないので、この問題が発生しないようにしてくれるわけです。
cannot borrow s as mutable because it is also borrowed as immutable
code:rust
fn main() {
{
foo();
}
}
fn foo() -> &String{
let x = String::from("hello");
&x
}
code:error
errorE0106: missing lifetime specifier --> src/main.rs:6:13
|
6 | fn foo() -> &String{
| ^ expected named lifetime parameter
|
= help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
help: consider using the 'static lifetime
|
6 | fn foo() -> &'static String{
| +++++++