93
60

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Rust その2Advent Calendar 2016

Day 5

Rustの所有権に親しむ

Last updated at Posted at 2016-12-05

遅れてしまいましたが、これは Rust その2 Advent Calendar 2016 の 5日目の記事です。

この記事はRustをあまり知らない人でも理解できると思います。

Rustを学んで所有権の仕組みに感動したので、
自分の理解の確認を兼ねて所有権のポイントをまとめました。
また、所有権によって得られるメリットを二つ、所有権の魅力として書いて見ました。

所有権の原則

RustはGCなし(ゼロコスト)でリソースを自動的に解放する仕組みを持っていて、それを実現する概念が所有権です。
Rustでは変数(束縛)がスコープを抜けるとき、リソースが解放されます。
スコープを抜けた時に全てのリソースが解放されなくてはいけないので、
変数に対する参照がある場合も、参照は参照元のスコープより長く存続できません。

3つのポイントで所有権を理解する

上記の原則を前提として、実際にプログラミングするときには
次の3つを意識すれば「借用チェッカとの争い」をしなくて済みそうです。

  1. 変数を束縛(代入)すると元の変数はアクセスできなくなる
  2. 参照は幾つでも作れる
  3. mutableは一つだけ
1.変数を束縛(代入)すると元の変数はアクセスできなくなる
let v1 = vec![1, 2, 3];
let v2 = v1;
println!("Hello, {}!", v1[1]); // error[E0382]: use of moved value: `v1`
2.参照は幾つでも作れる
let v1 = vec![1, 2, 3];
let v2 = &v1; // 代入時に&をつけると参照になります
let v3 = &v2;
println!("Hello, {}, {}, {} !", v1[0], v2[1], v3[2]); // no error
3.mutableは一つだけ
let mut v1 = vec![1, 2, 3];
let mut v2 = &mut v1; // &mutをつけるとmutable参照になり書き込みできます。
let mut v3 = &mut v1; // error[E0499]: cannot borrow `v1` as mutable more than once at a time
3'既に参照のある変数からはmutable参照できない
let mut v1 = vec![1, 2, 3];
let mut v2 = &mut v1; //no error
let v3 = &v1; //error[E0502]: cannot borrow `v1` as immutable because it is also borrowed as mutable
3''順番を入れ替えてもダメ(参照を既に持っているとmutable参照できない)
let mut v1 = vec![1, 2, 3];
let v3 = &v1; //no error
let mut v2 = &mut v1; //error[E0502]: cannot borrow `v1` as mutable because it is also borrowed as immutable
3'''mutable参照から通常の参照は作れるが、mutable参照に書き込みできなくなる
let mut v1 = vec![1, 2, 3];
let mut v2 = &mut v1;
let v3 = &v2; //no error
*v2 = vec![1, 5, 3]; //error[E0506]: cannot assign to `*v2` because it is borrowed
3''''もちろんスコープを抜ければ書き込める
let mut v1 = vec![1, 2, 3];
let mut v2 = &mut v1;
{
    let v3 = &v2; //no error
}
*v2 = vec![1, 5, 3]; //no error

所有権の魅力

変数のスコープを小さくする作用がある

束縛のデフォルトの動作は、束縛された変数に以後アクセスできなくなるという、ユニークで面倒くさいルールですが、
このルールは変数のスコープやメソッドの長さを小さく保つようにプログラマーに作用し、コードを綺麗にする効果があると思います。

不変と可変の使い分けがとても楽

mutの有無で変数自体への書き込みを制御するだけではなく、
変数にセットされたインスタンス変数(structのデータ)への書き込みも同時に制御できます。

驚きの機能。でもエラーメッセージはわかりにくい。
let mut mutable_vector: Vec<i32> = (0..10).collect();
mutable_vector.push(0); // no error

let imutable_vector: Vec<i32> = (0..10).collect();
imutable_vector.push(0); // error: cannot borrow immutable local variable `imutable_vector` as mutable

これは所有権とメソッド構文の合わせ技で実現しています。

Vec.push()の実装
pub fn push(&mut self, value: T) {
    // This will panic or abort if we would allocate > isize::MAX bytes
    // or if the length increment would overflow for zero-sized types.
    if self.len == self.buf.cap() {
        self.buf.double();
    }
    unsafe {
        let end = self.as_mut_ptr().offset(self.len as isize);
        ptr::write(end, value);
        self.len += 1;
    }
}

Rustはメソッドからインスタンス変数(structのデータ)への直接参照がありません。
自身のメンバ変数へアクセスする関数は第一引数で参照($selfや&mut self)を受け取るように定義します。
ただし関数を呼び出す時は第一引数を省略して「変数名.関数名(第二引数)」と書きます。

こう考えると、mutを外すとpushできなくなることも、「cannot borrow immutable local variable」と言われることも納得できます。

また、不変オブジェクトの全ての内部データが不変であることも確実に保証されることにもなります。
JavaやScalaでは不変と可変のオブジェクト変換は手間もコストもかかるので、この仕組みには感動しました。

おわり

Rustの所有権システム良いですね。

93
60
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
93
60

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?