gokayburuc.dev

Zig'de Error Union Operatorü

Error Union Operator in Zig

Error Union kavramı zig programlama dilinde ya hata ya da belirtilen veri türünde veri döndüren yapılar oluşturmak için kullanılır.

/// A common case for errors is a situation where we're expecting to
// have a value OR something has gone wrong. Take this example:
//
//     var text: Text = getText("foo.txt");
//
// What happens if getText() can't find "foo.txt"?  How do we express
// this in Zig?
//
// Zig lets us make what's called an "error union" which is a value
// which could either be a regular value OR an error from a set:
//
//     var text: MyErrorSet!Text = getText("foo.txt");
//
// For now, let's just see if we can try making an error union!
//
const std = @import("std");

const MyNumberError = error{TooSmall};

pub fn main() void {
    var my_number: MyNumberError!u8 = 5;

    // Looks like my_number will need to either store a number OR
    // an error. Can you set the type correctly above?
    my_number = MyNumberError.TooSmall;

    std.debug.print("I compiled!\n", .{});
}

Buradaki kodda ilk olarak farkedeceğimiz şey error union operatörü olarak bilinen ! operatörü ile yazılmış olan aşağıdaki kod yapısıdır:

    var my_number: MyNumberError!u8 = 5;

Bu kod blogu ya u8 formatında bir değer döndürür ya da daha önceden tanımlamış olduğumuz MyNumberError yapısına ait bir hata (error) döndürür.

const MyNumberError = error{TooSmall};

Yukarıda MyNumberError isimli bir error set tanımladık. Bu array içerisinde TooSmall gibi bir değer alabileceğini belirttik.

Daha sonrasında ise bu değeri doğrudan değişkene atadık.

    my_number = MyNumberError.TooSmall;

Programımız hatasız bir şekilde çalıştı. Çünkü sadece tanımlamalar yaptık ve bu aşamaya dek hata döndürebilecek hiçbir şey bildirmedik.

Error Union as Return in Zig

Bu kısımda bir fonksiyonun döndürdüğü değer olarak error union yapılarını değerlendireceğiz. Bu fonksiyonların döndürdüğü değerlerde hata kontrolü yaparken kullanacağımız yapılardır.

//
// One way to deal with error unions is to "catch" any error and
// replace it with a default value.
//
//     foo = canFail() catch 6;
//
// If canFail() fails, foo will equal 6.
//
const std = @import("std");

const MyNumberError = error{TooSmall};

pub fn main() void {
    const a: u32 = addTwenty(44) catch 22;
    const b: u32 = addTwenty(4) catch 22;

    std.debug.print("a={}, b={}\n", .{ a, b });
}

// Please provide the return type from this function.
// Hint: it'll be an error union.
fn addTwenty(n: u32) MyNumberError!u8 {
    if (n < 5) {
        return MyNumberError.TooSmall;
    } else {
        return n + 20;
    }
}

Şimdi kodu aşağıdaki şekilde parçalarına ayıralım.

const std = @import("std");

Zig’in standart kütüphanesini (std) içe aktarır. Böylece std.debug.print gibi fonksiyonları kullanabiliriz.

const MyNumberError = error{TooSmall};

Bu satır, MyNumberError isminde bir hata türü tanımlar. İçerisinde sadece bir hata durumu vardır: TooSmall. Bu bir error set’tir.

pub fn main() void {
    const a: u32 = addTwenty(44) catch 22;
    const b: u32 = addTwenty(4) catch 22;

    std.debug.print("a={}, b={}\n", .{ a, b });
}

Ana işlemlerimizin yapıldığı fonksiyonumuz ise yukarıdaki şekildedir. Burada da özel bir yazım görüyoruz:

    const a: u32 = addTwenty(44) catch 22;
    const b: u32 = addTwenty(4) catch 22;

Son olarak da sonuçları ekrana yazdırıyoruz:

    std.debug.print("a={}, b={}\n", .{ a, b });

İşlem sırasında diğer kontrol mekanizması olan addTwenty isimli fonksiyonu açıklayalım:

fn addTwenty(n: u32) MyNumberError!u8 {
    if (n < 5) {
        return MyNumberError.TooSmall;
    } else {
        return n + 20;
    }
}

addTwenty fonksiyonumuz u32 türünde n isimli bir değer alır. Bu değeri işlemlere tabi tutar ve bunun sonucunda MyNumberError hata setindeki değerlerden birini ya da u8 veri türünde bir değer döndürür.

#zig