型変換 (キャスト) とは、ある型を別の型に変換することです。Zenには暗黙の型変換と明示的な型変換があります。
暗黙の型変換は、型変換が安全であることが保証されている場合に、プログラマが明示的に書かなくても行われる型変換です。明示的な型変換は、Zenの組込み関数を用いてプログラマが明示的に行う型変換です。
ある型が求められている時に、異なる型を渡した際、暗黙の型変換が発生します。暗黙の型変換は、曖昧さがなく、その型変換が安全な場合のみ発生します。
次の場合について詳しく説明します。
整数型では、変換元の整数型の全ての値が、変換後の整数型で表現可能な場合、暗黙の型変換が働きます。わかりやすい変換は、より広いビット幅をもつ整数型 (符号なし→符号なし、または、符号あり→符号あり) です。
examples/ch11-advanced/cast/src/widening_number.zen:4:18
test "integer widening" {
var x: u8 = 250;
// u8 => u16
var y: u16 = x;
ok(x == y);
// u8 => u9
var z: u9 = x;
ok(x == z);
var xi: i8 = -127;
// i8 => i16
var yi: i16 = xi;
ok(xi == yi);
}
符号なしから符号ありに変換する場合、変換後の符号あり整数型は、変換元の符号なし整数型より、1ビット以上広いビット幅が必要です。次の例で変数z
はi9
型です。i9
型は-256から255の数値を表現でき、u8
型が取りうる0から255を全て表現できるため、暗黙の型変換が可能です。
examples/ch11-advanced/cast/src/widening_number.zen:20:27
test "unsigned integer to signed integer" {
var x: u8 = 255;
var y: i16 = x;
ok(y == 255);
var z: i9 = x;
ok(z == 255);
}
浮動小数点型も同様に、より広いビット幅を持つ型に変換できます。
examples/ch11-advanced/cast/src/widening_number.zen:29:34
test "float widening" {
var x: f16 = 1234.5678;
var y: f32 = x;
var z: f64 = y;
ok(x == z);
}
イミュータブルな型と volatile
はある型に対して、より厳しい制約を課しています。
ミュータブルなポインタ型 *mut T
を *T
に変換する場合や volatile
でない型 T
を volatile T
に変換する場合は暗黙の型変換が働きます。
examples/ch11-advanced/cast/src/qualification.zen:4:10
test "const qualification" {
var x: u32 = 42;
const ptr: *mut u32 = &mut x;
// *mut u32 => *u32
const const_ptr: *u32 = ptr;
ok(ptr == const_ptr);
}
align
はある型に対して、より小さいアライメントを指定すると制約が強くなります。そのため、より小さいアライメントを求める場合、暗黙の型変換が働きます。
examples/ch11-advanced/cast/src/qualification.zen:12:15
test "smaller alignment" {
var x: u32 align(8) = 42;
var y: u32 align(4) = x;
}
配列とポインタ (ファットポインタであるスライスを含む) とは、一定の条件を満たしている場合、暗黙の型変換が働きます。
配列へのポインタ (*[N]T
) からスライス ([]T
) へと変換する際、暗黙の型変換が働きます。
examples/ch11-advanced/cast/src/arrays_and_pointers.zen:5:13
test "pointer to an array to slice" {
var array = [5]u8{ 1, 2, 3, 4, 5 };
var a: *[5]u8 = &array;
// *[5]u8 => []u8
const s: []u8 = a;
equalSlices(u8, s, &[_]u8{ 1, 2, 3, 4, 5 });
// スライスの要素数は配列の要素数と同じ
ok(s.len == a.len);
}
配列へのポインタ (*[N]T
) から要素数が不明なポインタ ([*]T
) へと変換する際、暗黙の型変換が働きます。
examples/ch11-advanced/cast/src/arrays_and_pointers.zen:15:20
test "pointer to an array to unknown length pointer" {
var array = [5]u8{ 1, 2, 3, 4, 5 };
var a: *[5]u8 = &array;
const unknown_length: [*]u8 = a;
ok(unknown_length[4] == 5);
}
この暗黙の型変換は、C言語との相互運用で役立ちますが、一般的にはあまり利用すべきでありません。
オプション型 (?T
) に対して、T
とnull
から?T
に変換する場合、暗黙の型変換が働きます。
examples/ch11-advanced/cast/src/optional.zen:4:10
test "casting to optional" {
const x: ?u32 = 42;
const y: ?u32 = null;
ok(x.? == 42);
ok(y == null);
}
エラー共用体 (E!T
) に対して、エラー型E
とT
からE!T
に変換する場合、暗黙の型変換が働きます。
examples/ch11-advanced/cast/src/error_union.zen:5:16
const Error = error{
A,
B,
};
test "cast to error union" {
const x: Error!u32 = 42;
const y: Error!u32 = Error.A;
ok((try x) == 42);
err(Error.A, y);
}
コンパイル時計算可能な整数は、値が変換後の整数型の範囲内である場合、暗黙の型変換が働きます。コンパイル時計算可能な整数には、コンパイル時整数 (comptime_int
) と、コンパイル時計算可能な整数を代入したconst
変数とが、該当します。
examples/ch11-advanced/cast/src/comptime_known.zen:4:14
test "casting comptime known integers" {
// xは`comptime_int`型
const x = 255;
// comptime_int => u64
// `y`はコンパイル時計算可能な整数
const y: u64 = x;
// u64 => u8
// `y`の「値」は`255`であることがコンパイル時計算可能なため
// 暗黙の型変換が働く
const z: u8 = y;
}
タグ付き共用体を列挙型に型変換する場合、暗黙の型変換が働きます。
examples/ch11-advanced/cast/src/tagged_union.zen:4:19
const Tag = enum {
U32,
F64,
};
const Value = union(Tag) {
U32: u32,
F64: f64,
};
test "casting tagged union to enum" {
var value = Value{ .U32 = 42 };
// Value => Tag
var tag: Tag = value;
ok(tag == Tag.U32);
}
undefined
はあらゆる型に対して、暗黙の型変換が働きます。
組込み関数 @to
を使用することで安全な型変換が行えます。
const value = @to(u32, 42);
@to
で型変換できるのは、コンパイル時に安全と判断できる値のみです。例えば、よりビット幅の狭い整数から、よりビット幅の広い整数への変換です (u8
からu32
など) 。
下記のような関数形式の型変換は、0.8.0で廃止されました。
const value = u32(42);
組込み関数 @is
を使用すると戻り値をオプション型で返し、アンラップによって安全に型変換できない場合の処理を記述することができます。
例えば、次のコードのように構造体がインタフェース制約を満たしているかどうかのチェックに使用できます。
examples/ch11-advanced/cast/src/explicit_conversion.zen:43:114
const Food = interface {
pub fn eat() void;
};
const Drink = interface {
pub fn drink() void;
};
const Coffee = struct {
pub fn drink(self: *mut Coffee) void {
std.debug.warn("drink Coffee\n", .{});
}
};
const Pancake = struct {
pub fn eat(self: *mut Pancake) void {
std.debug.warn("eat Pancake\n", .{});
}
};
const Curry = struct {
pub fn eat(self: *mut Curry) void {
std.debug.warn("eat Curry\n", .{});
}
pub fn drink(self: *mut Curry) void {
std.debug.warn("drink Curry\n", .{});
}
};
test "query casting interface" {
// Coffee は Drink interface を実装しているため Drink にキャスト可能
var coffee = Coffee{};
// `@is` によるオプション型の戻り値をアンラップすることで interface が実装されていることを確認
var query_coffee = @is(Drink, coffee);
if (query_coffee) |casted| {
casted.drink();
} else {
return error.InvalidCast;
}
// Pancake は Food interface を実装しているため Food にキャスト可能
var pancake = Pancake{};
// 条件式に直接 `@is` を書くことでキャストできる interface を確認することが可能
if (@is(Drink, pancake)) |drink| {
drink.drink();
} else if (@is(Food, pancake)) |food| {
food.eat();
} else {
return error.InvalidCast;
}
// Curry は Food interface と Drink interface を両方実装しているため Food, Drink それぞれにキャスト可能
var curry = Curry{};
// 複数の interface を実装している構造体はそれぞれの interface へのキャストを確認することが可能
var query_curry = @is(Food, curry);
if (query_curry) |casted| {
casted.eat();
} else {
return error.InvalidCast;
}
query_curry = @is(Drink, curry);
if (query_curry) |casted| {
casted.drink();
} else {
return error.InvalidCast;
}
}
明示的な型変換はZen言語の組込み関数を利用して行います。明示的な型変換は、未定義動作を引き起す可能性があります。未定義動作を引き起こす条件と、対策方法は11.1 未定義動作を参照して下さい。
いくつかの明示的な型変換に対しては、言語レベルのチェックが入っています。例えば、@intCast
は引数の型に整数型しか受け付けません。
組込み関数 | 説明 |
---|---|
@bitCast | ビット表現を変更せずに型を変換します |
@alignCast | ポインタのアライメントを変換します |
@boolToInt | trueを1に、falseを0に、変換します |
@bytesToSlice | u8のスライスを別の要素型のスライスに変換します |
@enumToInt | 列挙型もしくはタグ付き共用体からタグの整数値を取得します |
@errSetCast | エラー型をサブセットのエラー型に変換します |
@errorToInt | エラー種別の整数値を取得します |
@floatCast | よりビット幅の少ない浮動小数点型に変換します |
@floatToInt | 浮動小数点値の整数部分を取得します |
@intCast | 整数型同士を変換します |
@intToEnum | 整数値から列挙型の値を取得します |
@intToError | 整数値からエラー種別を取得します |
@intToFloat | 整数値を浮動小数点値に変換します |
@intToPtr | アドレスをポインタに変換します |
@ptrCast | ポインタ型同士を変換します |
@ptrToInt | ポインタからアドレスに変換します |
@sliceToBytes | 何らかの要素型のスライスからu8のスライスへ変換します |
@truncate | 整数型同士の変換を行い、上位ビットを切り捨てます |
☰ 人の生きた証は永遠に残るよう ☰
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.