LoginSignup
77
43

More than 5 years have passed since last update.

[Rust]所有権、所有権、所有けんけんけけんけん

Last updated at Posted at 2018-09-05

Rustが良い、Rustが良いと一部のエンジニアから聞いていたのですが、どうせ文法的にはGolangScalaの中間くらいでしょう、くらいに考えていましたが、今は反省しています。大きなパラダイム・シフトがありました。

所有権

この概念がRustの中でも大きく理解を難解にさせていると思います。しかし一方でこの概念は今までのプログラミングで実行時に問題になってきた内容を前倒してコンパイル時に解決する、という解決策をRustは提供しているのです。

まずは普通の実行っぷりを書いてみます

src/main.rs
fn main() {
    let s = vec!["udon".to_string(),"ramen".to_string(),"soba".to_string()];
    let t = s;
    let u = t;
    println!("u[0] is: {}", u[0]);
}
実行結果

$ cargo run
   Compiling hello v0.1.0 
    Finished dev [unoptimized + debuginfo] target(s) in 0.47s
     Running `target/debug/hello`
u[0] is: udon

そしてこちらが所有権のエラーが発生するパターンです。コンパイルエラーになっています。

src/main.rs
fn main() {
    let s = vec!["udon".to_string(),"ramen".to_string(),"soba".to_string()];
    let t = s;
    let u = s;
    println!("u[0] is: {}", u[0]);
}
実行結果

$ cargo run
   Compiling hello v0.1.0 
warning: unused variable: `t`
 --> src/main.rs:3:9
  |
3 |     let t = s;
  |         ^ help: consider using `_t` instead
  |
  = note: #[warn(unused_variables)] on by default

error[E0382]: use of moved value: `s`
 --> src/main.rs:4:9
  |
3 |     let t = s;
  |         - value moved here
4 |     let u = s;
  |         ^ value used here after move
  |
  = note: move occurs because `s` has type `std::vec::Vec<std::string::String>`, which does not implement the `Copy` trait

error: aborting due to previous error

For more information about this error, try `rustc --explain E0382`.
error: Could not compile `hello`.

To learn more, run the command again with --verbose.

Rustでは通常の代入処理時に値の所有権の移動を行います。上の例ではs -> t -> uと代入処理を行っており、その順で所有権が移動、最終的にuにアクセスして要素を表示するものになっています。対して下の例では、s -> t, s -> uと代入を行っております。最初、s -> tと所有権が移動したタイミングでsは値にアクセスする権限を持っていません。そのため、代入処理ができないため、エラーが発生するのです。

Rustのすごいところはこれをコンパイラが解決しているところです。

もちろん代入処理だけでなく、所有権を失った変数にアクセスしようものならコンパイルエラーが発生します

src/main.rs
fn main() {
    let s = vec!["udon".to_string(),"ramen".to_string(),"soba".to_string()];
    let t = s; // sはここで所有権を失う
    let u = t;
    println!("s[0] is: {}", s[0]); // 所有権のないsにアクセス!
}

実行結果
$ cargo run
   Compiling hello v0.1.0 
warning: unused variable: `u`
 --> src/main.rs:4:9
  |
4 |     let u = t;
  |         ^ help: consider using `_u` instead
  |
  = note: #[warn(unused_variables)] on by default

error[E0382]: use of moved value: `s`
 --> src/main.rs:5:29
  |
3 |     let t = s;
  |         - value moved here
4 |     let u = t;
5 |     println!("s[0] is: {}", s[0]);
  |                             ^ value used here after move
  |
  = note: move occurs because `s` has type `std::vec::Vec<std::string::String>`, which does not implement the `Copy` trait

error: aborting due to previous error

For more information about this error, try `rustc --explain E0382`.
error: Could not compile `hello`.

To learn more, run the command again with --verbose.

大切なことなのでもう一度いいます。これをコンパイル時にRustは解決しています。
上記のコードをC++で書くと下記のようなコードになります。

main.cpp
#include <stdio.h>
#include <string>
#include <iostream>
#include <vector>

int main(){
    auto s = new std::vector<std::string>();s->push_back("udon");s->push_back("ramen");s->push_back("soba");
    auto t = s; s = NULL;
    auto u = s; s = NULL; 
    std::cout << (*s)[0] << std::endl;
    return 0;
}

でコンパイルできてしまいます。

実行結果
$ g++ -std=c++11 main.cpp 
$ ./a.out 
Segmentation fault: 11

実行時エラーになってしまうわけです。大規模になればなるほど、バグを探すのがめんどくさいあれです。
NULLを代入しなければもちろんアクセスできますが、メモリ解放の難解さを内包したままになるのでRustに寄せて記述しています。
この機能を持つプログラミング言語は今の所Rustくらいしかないのではないでしょうか。

ほら大規模開発をRustでしたくなってきたでしょう?とはいえ、まだライブラリが貧弱なので一筋縄にはいかないでしょうけども。

77
43
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
77
43