// dictionary
スコープ.
スコープ(scope)は、ある名前でその対象を参照できるソースコード上の範囲です。Rustのスコープの多くは波括弧{}のブロックで区切られ、letで宣言した変数が有効なのはlet文の直後からそのブロックの終わりまでです1。内側のブロックからは外側の名前が見えますが、その逆は成り立ちません。
スコープは名前の見え方だけでなく値の寿命も決めます。変数がスコープを抜けるとき、その変数がまだ値を所有していれば、値は自動的に破棄されます(所有権)。
fn main() {
let price = 300; // ここから main のブロックの終わりまで有効
{
let tax = 30; // このブロックの中だけ有効
println!("税込: {}円", price + tax); // 外側の price も見える
} // ここで tax がスコープを抜ける
// println!("{tax}"); // エラー: E0425(tax はもう見えない)
println!("本体価格: {}円", price);
}Playgroundで開く名前ごとの有効範囲
ブロックの終わりまで有効なのはletの変数だけではなく、束縛(binding)の種類ごとに範囲が決まっています1。
| 名前 | 有効な範囲 |
|---|---|
let文の変数 |
let文の直後からブロックの終わりまで |
| 関数の引数・クロージャの引数 | その本体の中 |
| for式のパターン束縛 | ループ本体の中 |
| if let式のパターン束縛 | その分岐のブロックの中(2024エディション以降は&&で続く後続の条件でも有効) |
| match式のパターン束縛 | そのアームのガードと本体 |
ブロック内で宣言した項目(fn・structなど) |
ブロックの先頭から終わりまで |
最後の行だけ性質が異なります。変数は宣言してから下でしか使えませんが、項目の名前はブロックの先頭から有効なため、定義より前の行からでも呼び出せます。
シャドーイングとの関わり
内側のブロックで同じ名前をlet宣言して覆い隠しても(シャドーイング)、その効果はそのブロックの終わりまでです。ブロックを抜ければ外側の変数が再び見えます。
所有権・借用との関わり
値の破棄はスコープを基準に行われます。ブロックなどのドロップスコープから制御が出るとき、そのスコープに属する変数は宣言と逆の順序で破棄されます2。
一方で借用の有効範囲はスコープの終わりとは一致しません。現在のコンパイラはNLL(非レキシカルライフタイム)により、借用を制御フロー上で「その参照が最後に使われる地点」までとして扱うため、参照を持つ変数がまだスコープ内にあっても、その後に元の値を変更できます。ただし参照を保持する型がDropを実装している場合は、スコープ終端での破棄も使用とみなされ、借用はスコープの終わりまで続きます2。
補足
項目が定義より前から呼べる例
fn main() {
println!("税込: {}円", with_tax(300)); // 定義より前だが呼べる
fn with_tax(price: i32) -> i32 {
price + price / 10
}
}Playgroundで開く破棄が宣言の逆順になる例
struct Receipt(&'static str);
impl Drop for Receipt {
fn drop(&mut self) {
println!("破棄: {}", self.0);
}
}
fn main() {
let first = Receipt("1枚目");
let second = Receipt("2枚目");
println!("会計終了: {} と {}", first.0, second.0);
} // 「破棄: 2枚目」→「破棄: 1枚目」の順に出力されるPlaygroundで開くローカル変数のスコープは項目の中に入り込まない
ブロック内で宣言したfnなどの項目からは、同じブロックのローカル変数を参照できません1。項目は周囲の環境を捕捉しないためで、捕捉したい場合はクロージャを使います。
fn main() {
let price = 300;
fn show() {
println!("{price}円"); // エラー: E0434(項目は環境を捕捉できない)
}
show();
}Playgroundで開くスコープに名前を持ち込む
名前をスコープに持ち込む手段がuse宣言です。useが作る名前も、書いたスコープの中でだけ有効です。
また、明示的に書かなくても常に見えている名前もあります。OptionやStringが何も書かずに使えるのは、標準ライブラリのプレリュードがすべてのモジュールのスコープに項目を持ち込んでいるからです1。
Footnotes
この辞書が使われているページ
backlinks 18