Progrust Library.

// 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

  1. Scopes — The Rust Reference ↩ ↩2 ↩3 ↩4

  2. Destructors — The Rust Reference ↩ ↩2

この辞書が使われているページ

backlinks 18

  1. 辞書impl Trait
  2. 辞書let-else文
  3. 辞書NLL(非レキシカルライフタイム)
  4. 辞書super
  5. 辞書use宣言
  6. 辞書シャドーイング
  7. 辞書ダングリング参照
  8. 辞書トレイト
  9. 辞書パニック
  10. 辞書ライフタイム
  11. 辞書外部パッケージ
  12. 辞書構造体
  13. 辞書借用
  14. 辞書借用チェッカー
  15. 辞書所有権
  16. 辞書定数
  17. 本動かして学ぶRustプログラミング問題集 › 第22章 ライフタイム
  18. 本動かして学ぶRustプログラミング問題集 › 第7章 所有権とムーブ