Rust の移動セマンティクス

Nilesh Katuwal 2022年6月7日
Rust の移動セマンティクス

この記事では、Rust の移動セマンティクスについて学習します。

Rust でセマンティクスを動かす

Rust では、すべてのタイプが変更可能であり、すべての移動操作は、新しい場所にある元のデータのビットコピーと同等です。Move は、キャプチャされた変数を参照によって、または可変参照を値によってキャプチャされた変数に変換します。

スレッドが関係する場合、移動が一般的に使用されます。

Rust 除去プロセスも破壊的です。たとえば、変数を残した後、その変数はコードで使用できなくなります。

Rust では、移動は、オブジェクト自体のデータをコピーし、元のオブジェクトを破棄しないことによって一貫して実装されます。オブジェクトの管理対象リソースをコピーしたり、カスタムコードを実行したりして実装することはできません。

Rust は、移動セマンティクスを必要としないタイプの例外を作成します。代わりに、値にはそれを表すために必要なすべての情報が含まれ、i32 などの値によってヒープ割り当てやリソースが管理されることはありません。

これらのタイプは、特別なコピー特性を実装します。これは、コピーが安価であり、関数に渡したり、割り当てを管理したりするためのデフォルトの方法であるためです。

fn foo(bar: i32) {
    // Implementation
}

let var: i32 = 9;
foo(var); // copy
foo(var); // copy
foo(var); // copy

デフォルトの関数呼び出しは、String などの Copy 以外のタイプの移動セマンティクスを使用します。Rust では、変数が移動されると、その存続期間が途中で終了します。

コンパイル時に、移動によってブロックの最後にあるデストラクタ呼び出しが置き換えられるため、String に次のコードを記述するとエラーになります。

fn foo(bar: String) {
    // Implementation
}

let var: String = "Hello".to_string();
foo(var); // Move
foo(var); // Compile-Time Error
foo(var); // Compile-Time Error

クローンラストには実装が必要ですが、すべての移動で同じです。値のメモリをコピーし、元の値のデストラクタを呼び出さないでください。

そして Rust では、この正確な実装により、すべてのタイプが移動可​​能です。移動不可能なタイプは存在しません(ただし、移動不可能な値は存在します)。バイトは、以前の場所と同じように効果的に新しい場所で、値が管理しているリソースに関する情報(ポインターなど)をエンコードする必要があります。

Rust には、特定の値が二度と移動しないことをタイプシステムで示すピン留めと呼ばれるメカニズムがあります。これは、自己参照値を実装するために使用でき、非同期で使用されます。

例:

fn destroy_box(z: Box<i32>) {
    println!("The box that contains {} is destroyed", z);
}

fn main() {
    let a = 5u32;
    let b = a;
    println!("a is {}, and y is {}", a, b);

    let m = Box::new(5i32);

    println!("a contains: {}", m);
    let n = m;
    destroy_box(n);
}

destroy_box 関数は、上記のソースコードでヒープに割り当てられたメモリの所有権を取得します。まず、z が破棄され、メモリが解放されます。

m は、ヒープに割り当てられた整数へのポインタです。

出力:

a is 5, and y is 5
a contains: 5
The box that contains 5 is destroyed