変数や定数といった識別子にはスコープ (有効な範囲) が決められています。ここでは、スコープの種別とその使い方について説明します。一般的には、可能な限り識別子のスコープを狭くするべきです。そうすることで、識別子が誤って使用されるのを防ぐことができます。
Zenは、より外側にある変数を隠す (シャドーイングする) 変数の定義を許しません。次のコードでは、グローバルなx
と、関数ローカルなx
を定義しようとしています。
const x: u32 = 0;
fn doSomething() void {
const x: u32 = 0;
}
これは、次のコンパイルエラーになります。
error[E07000]: redeclaration of 'x'
const x: u32 = 0;
~
グローバル変数はルートレベルにある変数です。グローバル変数の初期値は、コンパイル時計算可能でなければなりません。グローバル変数がconst
の場合、その値はコンパイル時計算可能な変数として利用できます。var
は初期値がコンパイル時に決まる、実行時計算可能な値という扱いになります。
下のコードで、var global1: u32 = constant + 1;
はconstant + 1
がコンパイル時計算可能であるためコンパイルが通ります。しかしvar global2: u32 = global1 + 1;
は、global1 + 1
が実行時計算可能になるため、コンパイルエラーになります。
examples/ch04-basic-syntax/src/scope.zen:4:14
const constant: u32 = 0;
var global1: u32 = constant + 1;
// Compile error: unable to evaluate constant expression
// var global2: u32 = global1 + 1;
test "global variables" {
comptime {
ok(constant == 0);
}
ok(global1 == 1);
}
構造体、共用体、列挙型の中にもグローバル変数が定義できます。
examples/ch04-basic-syntax/src/scope.zen:16:18
const Struct = struct {
var global_in_struct: u32 = 0;
};
このことを応用して、ある関数内からしか触ることのできないグローバル変数を作ることができます。
examples/ch04-basic-syntax/src/scope.zen:69:81
fn cStatic() u32 {
const CStatic = struct {
var value: u32 = 0;
};
CStatic.value += 1;
return CStatic.value;
}
test "c static" {
ok(cStatic() == 1);
ok(cStatic() == 2);
}
関数およびブロック内に定義した変数はローカル変数です。ローカル変数はそのスコープを越えて存在することはできません。
examples/ch04-basic-syntax/src/scope.zen:28:43
fn functionScope() void {
const function_scope: u32 = 1;
{
const block_scope: u32 = 2;
// 中のスコープでは外のスコープの変数を利用できる
ok(block_scope + function_scope == 3);
} // `block_scope`が利用可能なのはここまで
// Compile error: use of undeclared identifier 'block_scope'
// ok(block_scope == 2);
}
// Compile error: use of undeclared identifier 'function_scope'
// `function_scope`は`functionScope`関数の中でのみアクセス可能
// const y: u32 = function_scope;
threadlocal
キーワードを付けた変数はスレッドローカル変数になります。すなわち、スレッドごとに異なるメモリ領域を持ちます。別スレッドがスレッドローカルな変数に行った変更は、自身のスレッドローカルな変数に影響を与えません。
examples/ch04-basic-syntax/src/scope.zen:49:67
threadlocal var thread_scope: u32 = 1;
fn incThreadLocal(context: void) void {
ok(thread_scope == 1);
thread_scope += 1;
ok(thread_scope == 2);
}
test "thread local variable" {
incThreadLocal({});
ok(thread_scope == 2);
// 別スレッドを立ち上げて`thread_scope`を変更する
const thread = try std.threading.Thread.spawn({}, incThreadLocal);
thread.wait();
// 別スレッドで`thread_scope`を変更しても影響しない
ok(thread_scope == 2);
}
スレッドローカルなconst
を作ることは意味を為さないでしょう。イミュータブルな値を読み込むだけであれば、グローバルなconst
で十分なためです。
組込み関数の@import
は別ソースファイル中でpub
キーワードをつけて宣言された変数や関数を、インポートしたソースファイルから利用できるようにします。Zenのソースファイルは、暗黙的に構造体として扱われます。この暗黙の構造体は、名前空間と呼べるでしょう。多くの場合、この名前空間に新しい名前を定義します。インポートしたソースファイル内の変数は、その名前空間内にスコープを持ちます。
const namespace = @import("source.zen");
@import
の引数には、Zenのソースファイルを、絶対パスもしくは相対パスで与えます。std
とbuiltin
だけは特別で、どこからでも@import("std")
と@import("builtin")
でインポートできます。
ある型に対して、別名をつけることができます。これを型エイリアスと呼びます。例えば、[]u8
にString
というエイリアスを付けるには、次のようにします。
const String = []u8;
型エイリアスは、エイリアスが定義されたスコープ内でのみ有効です。
@import
でインポートした場合、メンバアクセス演算子 (.
) と使用して、名前空間.関数
や名前空間.変数
といった形で、pub
な関数や変数を利用します。しかし、インポートする名前空間内全てのpub
アイテムをそのまま使いたい場合があります。
そのような時はusingnamespace
を利用できます。
例えば、今次のようなadd_func.zen
があるとします。
pub fn add(a: u32, b: u32) u32 {
return a + b;
}
このadd
関数を、名前空間を通さずに利用するには、次のようにします。
usingnamespace @import("add_func.zen");
test "usingnamespace" {
ok(add(1, 2) == 3);
}
☰ 人の生きた証は永遠に残るよう ☰
Copyright © 2018-2020 connectFree Corporation. All rights reserved. | 特定商取引法に基づく表示
Zen, the Zen three-circles logo and The Zen Programming Language are trademarks of connectFree corporation in Japan and other countries.