Comptime

Los bloques de código pueden ser ejecutados de manera forzada en tiempo de compilación usando la palabra clave comptime. En este ejemplo, las variables x e y son equivalentes.

fn fibonacci(n: u16) u16 {
    if (n == 0 or n == 1) return n;
    return fibonacci(n - 1) + fibonacci(n - 2);
}

test "Bloques comptime" {
    const x = comptime fibonacci(10);
    const y = comptime blk: {
        break :blk fibonacci(10);
    };
    try expect(y == 55);
    try expect(x == 55);
}

Los literales enteros son del tipo comptime_int. Estos son especiales en el sentido de que no tienen tamaño (¡no se pueden usar en tiempo de ejecución!) y tienen precisión arbitraria. Los valores comptime_int se convierten a cualquier tipo de entero que pueda contenerlos. También se convierten a floats. Los literales de caracteres son de este tipo.

test "comptime_int" {
    const a = 12;
    const b = a + 10;

    const c: u4 = a;
    const d: f32 = b;

    try expect(c == 12);
    try expect(d == 22);
}

También está disponible comptime_float, que internamente es un f128. Estos no pueden ser convertidos a enteros, incluso si contienen un valor entero.

Los tipos en Zig son valores del tipo type. Estos están disponibles en tiempo de compilación. Anteriormente los hemos encontrado mediante @TypeOf y comparándolos con otros tipos, pero podemos hacer más.

test "ramificación en tipos" {
    const a = 5;
    const b: if (a < 10) f32 else i32 = 5;
    try expect(b == 5);
    try expect(@TypeOf(b) == f32);
}

Los parámetros de función en Zig pueden ser etiquetados como comptime. Esto significa que el valor pasado a ese parámetro de función debe ser conocido en tiempo de compilación. Hagamos una función que devuelve un tipo. Observa cómo esta función está en PascalCase, ya que devuelve un tipo.

fn Matrix(
    comptime T: type,
    comptime width: comptime_int,
    comptime height: comptime_int,
) type {
    return [height][width]T;
}

test "returning a type" {
    try expect(Matrix(f32, 4, 4) == [4][4]f32);
}

Podemos reflexionar sobre los tipos usando el built-in @typeInfo, que toma un tipo y devuelve una unión etiquetada. Este tipo de unión etiquetada se puede encontrar en std.builtin.TypeInfo (información sobre cómo utilizar importaciones y std más tarde).

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

fn addSmallInts(comptime T: type, a: T, b: T) T {
    return switch (@typeInfo(T)) {
        .ComptimeInt => a + b,
        .Int => |info| if (info.bits <= 16)
            a + b
        else
            @compileError("ints too large"),
        else => @compileError("only ints accepted"),
    };
}

test "typeinfo switch" {
    const x = addSmallInts(u16, 20, 30);
    try expect(@TypeOf(x) == u16);
    try expect(x == 50);
}

Podemos usar la función @Type para crear un tipo a partir de un @typeInfo. @Type está implementado para la mayoría de los tipos, pero notablemente no está implementado para enums, unions, funciones y structs.

Aquí se usa la sintaxis de estructura anónima con .{}, porque la T en T{} puede ser inferida. Las estructuras anónimas se tratarán en detalle más adelante. En este ejemplo, obtendremos un error de compilación si la etiqueta Int no está configurada.

fn GetBiggerInt(comptime T: type) type {
    return @Type(.{
        .Int = .{
            .bits = @typeInfo(T).Int.bits + 1,
            .signedness = @typeInfo(T).Int.signedness,
        },
    });
}

test "@Type" {
    try expect(GetBiggerInt(u8) == u9);
    try expect(GetBiggerInt(i31) == i32);
}

Devolver un tipo de estructura es cómo se crean estructuras de datos genéricas en Zig. El uso de @This es necesario aquí, lo que obtiene el tipo de la estructura, unión o enum más interna. Aquí también se usa std.mem.eql, que compara dos slices.

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

fn Vec(
    comptime count: comptime_int,
    comptime T: type,
) type {
    return struct {
        data: [count]T,
        const Self = @This();

        fn abs(self: Self) Self {
            var tmp = Self{ .data = undefined };
            for (self.data, 0..) |elem, i| {
                tmp.data[i] = if (elem < 0)
                    -elem
                else
                    elem;
            }
            return tmp;
        }

        fn init(data: [count]T) Self {
            return Self{ .data = data };
        }
    };
}

const eql = @import("std").mem.eql;

test "vector generico" {
    const x = Vec(3, f32).init([_]f32{ 10, -10, 5 });
    const y = x.abs();
    try expect(eql(f32, &y.data, &[_]f32{ 10, 10, 5 }));
}

Los tipos de los parámetros de función también pueden ser inferidos usando anytype en lugar de un tipo. Luego se puede usar @TypeOf en el parámetro.

fn plusOne(x: anytype) @TypeOf(x) {
    return x + 1;
}

test "parámetro de función inferido" {
    try expect(plusOne(@as(u32, 1)) == 2);
}

Comptime también introduce los operadores ++ y ** para concatenar y repetir arrays y slices. Estos operadores no funcionan en tiempo de ejecución.

Edita esta pagina Última actualización: 5/15/2024 por Vital