🌟

Zigでコンパイルエラーになって書き方がわからないとき

2023/01/16に公開

Zig言語は型に厳しい

Zigは型に関して厳しい言語です。
以下のようなコードを書いたときに、たぶん型変換のことでエラーになるだろうなあと思いつつコンパイルしてみると

a.zig
const std = @import("std");
const expect = std.testing.expect;

fn func(x: i32) u8 {
    return x & 0xff;
}

test "truncate" {
    try expect(func(0x1234) == 0x34);
}

やはり、こんなエラーになりました。

$ zig test a.zig
a.zig:5:14: error: expected type 'u8', found 'i32'
    return x & 0xff;
           ~~^~~~~~
a.zig:5:14: note: unsigned 8-bit int cannot represent all possible signed 32-bit values
a.zig:4:17: note: function return type declared here
fn func(x: i32) u8 {
                ^~
referenced by:
    test.truncate: a.zig:9:16
    remaining reference traces hidden; use '-freference-trace' to see all reference traces

Cで書いてZigに変換してみる

こんなときはC言語で書いてみて、zig tranlate-cでZigに変換すると答えがわかります。

a.c
#include <stdint.h>

uint8_t func(int32_t x) 
{
	return x & 0xff;
}

コンパイルエラーにならないことを確認。

$ zig cc -c a.c
$ 

zig translate-cは引数にインクルードファイルのディレクトリを指定する必要があるのですが、毎回するのが面倒なのでbash scriptにしました。gistに置きました。

$ c2zig a.c

変換結果はこのようになっていました。

a_translated.zig
  ...
  
pub export fn func(arg_x: i32) u8 {
    var x = arg_x;
    return @bitCast(u8, @truncate(i8, x & @as(c_int, 255)));
}
  ...

これを参考にして、以下のようにa.zigを修正します。

a.zig
const std = @import("std");
const expect = std.testing.expect;

fn func(x: i32) u8 {
    return @bitCast(u8, @truncate(i8, x & 0xff));
}

test "truncate" {
    try expect(func(0x1234) == 0x34);
}

テスト実行。

$ zig test a.zig
All 1 tests passed.

OKでした。

よく考えてみると@truncateすることは & 0xff と同じことなので冗長を省くと以下のようにできます。

fn func(x: i32) u8 {
    return @bitCast(u8, @truncate(i8, x));
}

関連

https://zenn.dev/tetsu_koba/articles/c039b865114b93
https://zenn.dev/tetsu_koba/articles/95ce0deb9a6704
https://zenn.dev/tetsu_koba/articles/421198dc669f19
https://zenn.dev/tetsu_koba/articles/214644ef10fc52
https://zenn.dev/tetsu_koba/articles/a996108d5a9fcd

Discussion