Moveのすべての変数にはスコープと所有者があります。スコープは変数が有効なコードの範囲であり、所有者はこの変数が属するスコープです。所有者のスコープが終了すると、変数は削除されます。これはMoveの基本的な概念であり、その動作を理解することが重要です。
関数スコープで定義された変数は、このスコープによって所有されます。ランタイムは関数スコープを通過し、すべての式とステートメントを実行します。関数スコープが終了すると、その中で定義された変数は削除または解放されます。
module book::ownership {
public fun owner() {
let a = 1; // aは`owner`関数によって所有される
} // aはここで削除される
public fun other() {
let b = 2; // bは`other`関数によって所有される
} // bはここで削除される
#[test]
fun test_owner() {
owner();
other();
// aとbはここでは無効
}
}
上記の例では、変数a
はowner
関数によって所有され、変数b
はother
関数によって所有されます。これらの関数が呼び出されると、変数が定義され、関数が終了すると変数が破棄されます。
owner
関数を変更して変数a
を返すようにすると、a
の所有権は関数の呼び出し元に移ります。
module book::ownership {
public fun owner(): u8 {
let a = 1; // aはここで定義される
a // スコープが終了し、aが返される
}
#[test]
fun test_owner() {
let a = owner();
// aはここで有効
} // aはここで削除される
}
さらに、変数a
を別の関数に渡すと、a
の所有権はこの関数に移ります。この操作を行うとき、値をあるスコープから別のスコープに「移動」します。これを「ムーブセマンティクス」とも呼びます。
module book::ownership {
public fun owner(): u8 {
let a = 10;
a
} // aは返される
public fun take_ownership(v: u8) {
// vは`take_ownership`によって所有される
} // vはここで削除される
#[test]
fun test_owner() {
let a = owner();
take_ownership(a);
// aはここでは無効
}
}
各関数にはメインスコープがあり、ブロックを使用してサブスコープを持つこともできます。ブロックはステートメントと式のシーケンスであり、それ自身のスコープを持ちます。ブロックで定義された変数はこのブロックによって所有され、ブロックが終了すると変数は削除されます。
module book::ownership {
public fun owner() {
let a = 1; // aは`owner`関数のスコープによって所有される
{
let b = 2; // bはブロックによって所有される
{
let c = 3; // cはブロックによって所有される
}; // cはここで削除される
}; // bはここで削除される
// a = b; // エラー: bはここでは無効
// a = c; // エラー: cはここでは無効
} // aはここで削除される
}
しかし、ブロックの戻り値を使用する場合、変数の所有権はブロックの呼び出し元に移ります。
module book::ownership {
public fun owner(): u8 {
let a = 1; // aは`owner`関数のスコープによって所有される
let b = {
let c = 2; // cはブロックによって所有される
c // cが返される
}; // cはここで削除される
a + b // aとbはここで有効
}
}
Moveの一部の型は「コピー可能」であり、所有権を移さずにコピーできます。これは、整数やブール値のように小さくてコピーが安価な型に便利です。Moveコンパイラは、これらの型が関数に渡されたり、関数から返されたり、スコープに「移動」されて元のスコープでアクセスされたりするときに自動的にコピーします。