Optionals

Los optionals usan la sintaxis ?T y se utilizan para almacenar el valor null o un valor de tipo T.

test "optional" {
    var found_index: ?usize = null;
    const data = [_]i32{ 1, 2, 3, 4, 5, 6, 7, 8, 12 };
    for (data, 0..) |v, i| {
        if (v == 10) found_index = i;
    }
    try expect(found_index == null);
}

Los optionals admiten la expresión orelse, que actúa cuando el optional es null. Esto desempaqueta el optional a su tipo hijo.

test "orelse" {
    const a: ?f32 = null;
    const fallback_value: f32 = 0;
    const b = a orelse fallback_value;
    try expect(b == 0);
    try expect(@TypeOf(b) == f32);
}

.? es una forma abreviada para orelse unreachable. Esto se usa cuando sabes que es imposible que un valor opcional sea null, y usar esto para desempaquetar un valor null es un comportamiento ilegal detectable.

test "orelse unreachable" {
    const a: ?f32 = 5;
    const b = a orelse unreachable;
    const c = a.?;
    try expect(b == c);
    try expect(@TypeOf(c) == f32);
}

Tanto las expresiones if como los bucles while admiten tomar valores opcionales como condiciones, lo que te permite “capturar” el valor interno no nulo.

Aquí usamos una captura opcional de carga if; a y b son equivalentes aquí. if (b) |valor| captura el valor de b (en los casos donde b no es null) y lo hace disponible como valor. Como en el ejemplo de la unión, el valor capturado es inmutable, pero aún podemos usar una captura de puntero para modificar el valor almacenado en b.

test "captura opcional de carga en un if" {
    const a: ?i32 = 5;
    if (a != null) {
        const value = a.?;
        _ = value;
    }

    var b: ?i32 = 5;
    if (b) |*value| {
        value.* += 1;
    }
    try expect(b.? == 6);
}

Y con while:

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

var numbers_left: u32 = 4;
fn eventuallyNullSequence() ?u32 {
    if (numbers_left == 0) return null;
    numbers_left -= 1;
    return numbers_left;
}

test "while null capture" {
    var sum: u32 = 0;
    while (eventuallyNullSequence()) |value| {
        sum += value;
    }
    try expect(sum == 6); // 3 + 2 + 1
}

Los tipos opcionales de puntero y de slice no ocupan memoria adicional en comparación con los no opcionales. Esto se debe a que internamente utilizan el valor 0 del puntero para null.

Así es como funcionan los punteros nulos en Zig: deben ser desempaquetados a un tipo no opcional antes de desreferenciarlos, lo que evita que ocurran desreferencias de punteros nulos accidentalmente.

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