はじめに
Rust におけるスコープとブロックは、プログラムの構造を理解する上で重要な概念です。適切なスコープとブロックを使用することで変数や関数などの有効範囲を制御し、プログラムをより効率的で可読性の高いものにすることができます。
スコープとブロック
スコープ(Scope)はプログラム内で定数や変数、関数などの要素が有効である(使用できる)範囲を指します。一方、ブロック(Block)は {}
で囲まれた領域を指します。
スコープの基本
Rust では {}
を使用して独自のスコープ(ローカルスコープ)を作成します。関数、ループ、条件式などでローカルスコープを作成することもできます。
{}
で囲まれた領域はブロックと呼ばれ、ブロックの中で宣言された変数や関数はそのブロック内でのみ有効です。
ローカルスコープ外の範囲はグローバルスコープと呼ばれます。
グローバルスコープ
グローバルスコープで宣言された変数や関数はプログラム全体で有効です。つまり、プログラムのどの場所からでも変数や関数を使用することができます。
// グローバルスコープ
const A: i32 = 20; // 定数
fn main() {
println!("{}", A); // 定数を使用
}
この例では、定数 A
はグローバルスコープで宣言されており、どのスコープからでもアクセスが可能です。
ローカルスコープ
ローカルスコープで宣言された変数や関数はそのスコープ内でのみ有効です。Rust ではブロックや関数、ループ、条件式でローカルスコープを作成できます。
fn main() {
let a = 10;
{// 内側のスコープ
let b = 20;
// 現在のスコープの外側で宣言した変数(a)は使用できる
println!("a: {}, b: {}", a, b);
}
// 内側のスコープで宣言した変数(b)は使用できない
// println!("b: {}", b); // エラー: b はスコープ外
}
この例では、変数 b
は内側のスコープで宣言され、そのスコープ内でのみ有効です。スコープが終了すると、変数 b
は解放され、アクセスできなくなります。
スコープのネスト
Rust ではスコープをネストすることができます。内側のスコープでは、外側のスコープで宣言された変数にアクセスできますが、その逆はできません。
fn main() {
let x = 5;
{// 内側のスコープ1
let y = 10;
{// 内側のスコープ2
let z = 15;
println!("x: {}, y: {}, z: {}", x, y, z);
}
// println!("z: {}", z); // エラー: zはスコープ外
}
// println!("y: {}", y); // エラー: yはスコープ外
}
この例では、変数 x
は最も外側のスコープに宣言されているため、どの内側のスコープからでもアクセスできます。一方、変数 y
は内側のスコープ 1 で宣言されているため、その内側のスコープ 2 からアクセスできますが、最も外側のスコープからはアクセスできません。変数 z
は内側のスコープ 2 で宣言されているため、そのスコープ内でのみ有効です。
式としてのブロック
Rust におけるブロックは、単にスコープを形成するだけでなく、式(Expression)としても扱うことができます。
式と文
式(Expression)とは、値を生成するコードの断片です。5 + 3
などです。
一方、文(Statement)はひとつの完結した命令です。文は値を返さず、単に実行される命令です。Rust において ;
は文の終わりを表す記号です。
Expression
-----
let x = 5 + 3; // `5 + 3` は式であり、`let x = 5 + 3;` は文
--------------
statement
式としてブロックを使うことで、複数の文をまとめて 1 つの式として評価することができます。
fn main() {
let x = {
let y = 5;
let z = 7;
// ブロックの最後に ; がないので式となり、その結果がブロックの値となる
y + z
};
println!("x = {}", x); // x は 12 になる
}
シャドーイング
Rust では、同じ名前の変数を再度宣言することができます。このプロセスをシャドーイング(Shadowing)と呼びます。
fn main() {
let x = 5;
println!("最初の x: {}", x); // 5
// 同じスコープでシャドーイング
let x = x + 1;
println!("シャドーイング後の x: {}", x); // 6
{
// 内側のスコープでシャドーイング
let x = x * 2;
println!("内側スコープの x: {}", x); // 12
}
println!("最終的な x: {}", x); // 6
}
この例では、変数 x
の値は最初 5 ですが、その後シャドーイングにより新しい x
が作成されます。内側のスコープでもシャドーイングが行われ、新しい値が計算されます。ただし、スコープ内部でシャドーイングしてもスコープ外部に影響を与えません。