Rustの所有権、慣れるまで時間がかかりそう。

開発環境

> rustc --version
rustc 1.44.1 (c7087fe00 2020-06-17)

タプル

異なる型の値の集合。
所有権周りを理解していく。

例1

fn main() {
    let t1 = ("a", "b");
    let t2 = t1;
    println!("{}", t1.0);  // a
}

t1はタプルに束縛され、所有権を保持。
t2t1のコピーに束縛され、所有権を保持。
t1は所有権を保持しているため、println!で値を出力できる。

なぜ所有権が移動せずに、コピーが行われ保持されるのか。

If every type inside a tuple implements one of the following traits, then a tuple itself also implements it.
- …
- Copy
(from tuple - Rust)

タプル内の値の型が全てCopyトレイトを実装している場合、タプル自体もそれが適用される。

例2

タプルに自作構造体を入れた。

// コンパイルエラー
fn main() {
    struct Point {
        x: i32,
        y: i32,
    }

    // println!に流すためのトレイト
    impl std::fmt::Display for Point {
        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
            write!(f, "({}, {})", self.x, self.y)
        }
    }

    let u1 = (Point{x: 0, y: 1}, "b");
    let u2 = u1;
    println!("{}", u1.0);  // error[E0382]: borrow of moved value: `u1`
}

u1はタプルに束縛され、所有権を保持。
u2u1のタプルに束縛され、u1から所有権を移動。
u1は所有権を保持していないため、println!で値を出力できない。

自作構造体PointにはCopyトレイトが実装されていないため、大元のタプルもCopyトレイトが適用されない。

例3

PointにCopyトレイト, Cloneトレイトを実装した。

Copyトレイトを実装するにはCloneトレイトを実装しなければならない。
Swiftでいえば、プロトコルが二層(直列)になっているイメージ。

fn main() {
    struct Point {
        x: i32,
        y: i32,
    }

    impl std::fmt::Display for Point {
        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
            write!(f, "({}, {})", self.x, self.y)
        }
    }

    impl Copy for Point { }

    impl Clone for Point {
        fn clone(&self) -> Self {
            *self  // `*`は参照されている値を得るため
        }
    }

    let v1 = (Point{x: 0, y: 1}, "b");
    let v2 = v1;
    println!("{}", v1.0);  // (0, 1)
}

v1はタプルに束縛され、所有権を保持。
v2v1のコピーに束縛され、所有権を保持。
v1は所有権を保持しているため、println!で値を出力できる。

 


impl Clone for Point {
    fn clone(&self) -> Self {
        *self  // `*`は参照されている値を得るため
    }
}

コメントでも書いたが、*(アスタリスク)は参照自体ではなく、参照されている値を得るために使う。

// コンパイルエラー
fn main() {
    let x = 7;
    let y = &x;
    assert_eq!(7, y);  // error[E0277]: can't compare `{integer}` with `&{integer}`
}

 

fn main() {
    let x = 7;
    let y = &x;
    assert_eq!(7, *y);  // assertが発生しない → 値の比較ができており同値である
}

参考