Type ‣ Error set ᴾᴴᴾ - chung-leong/zigar GitHub Wiki

JavaScript | PHP


Error sets are used by Zig functions to indicate they've failed to perform an operation for one reason or another. They are like enums, unique integers with assigned meanings. In PHP, they are represented as unique Exception objects. Typically they form part of an error union returned by a function:

pub const FileOpenError = error{
    AccessDenied,
    OutOfMemory,
    FileNotFound,
};

pub const AllocationError = error{
    OutOfMemory,
};

pub fn fail(reason: u32) !bool {
    return switch (reason) {
        1 => FileOpenError.AccessDenied,
        2 => FileOpenError.OutOfMemory,
        3 => FileOpenError.FileNotFound,
        else => false,
    };
}
<?php

$m = zigar_use(__DIR__ . '/error-set-example-1.zig');

try {
    $m->fail(2);
} catch (Exception $e) {
    echo $e->getMessage(), "\n";
    debug_zval_dump($e == $m->FileOpenError->OutOfMemory);
}
out of memory
bool(true)

Errors with the same name are represented by the same exception object. They can appear in multiple error sets. Individual errors are not instances of the error set class containing them (unlike enum items):

<?php

$m = zigar_use(__DIR__ . '/error-set-example-1.zig');

debug_zval_dump($m->FileOpenError->OutOfMemory == $m->AllocationError->OutOfMemory);
debug_zval_dump($m->FileOpenError->OutOfMemory instanceof $m->FileOpenError);
debug_zval_dump($m->AllocationError->OutOfMemory instanceof $m->AllocationError);
bool(true)
bool(false)
bool(false)

Error casting

You can obtain the numeric value of an error through casting:

<?php

$m = zigar_use(__DIR__ . '/error-set-example-1.zig');

debug_zval_dump((int) $m->FileOpenError->AccessDenied);
debug_zval_dump((int) $m->FileOpenError->OutOfMemory);
debug_zval_dump((int) $m->FileOpenError->FileNotFound);
debug_zval_dump((int) $m->AllocationError->OutOfMemory);
int(16)
int(32)
int(37)
int(32)

Note how $m->FileOpenError->OutOfMemory and AllocationError.OutOfMemory give us the same number. This is because they are the same object.

Errors can be casted into strings as well:

<?php 

$m = zigar_use(__DIR__ . '/error-set-example-1.zig');

debug_zval_dump((string) $m->FileOpenError->AccessDenied);
debug_zval_dump((string) $m->FileOpenError->OutOfMemory);
debug_zval_dump((string) $m->FileOpenError->FileNotFound);
debug_zval_dump((string) $m->AllocationError->OutOfMemory);
string(13) "access denied" refcount(4)
string(13) "out of memory" refcount(5)
string(14) "file not found" refcount(4)
string(13) "out of memory" refcount(4)

The reverse is also possible. Strings and numbers can be casted into errors:

<?php

$m = zigar_use(__DIR__ . '/error-set-example-1.zig');

debug_zval_dump($m->FileOpenError(16));
debug_zval_dump($m->FileOpenError('access denied'));
debug_zval_dump($m->FileOpenError('AccessDenied'));
debug_zval_dump($m->FileOpenError(32));

object(ES0)#8 (1) refcount(5){
  ["error"]=>
  string(13) "access denied" refcount(4)
}
object(ES0)#8 (1) refcount(5){
  ["error"]=>
  string(13) "access denied" refcount(3)
}
object(ES0)#8 (1) refcount(5){
  ["error"]=>
  string(13) "access denied" refcount(2)
}
object(ES0)#9 (1) refcount(5){
  ["error"]=>
  string(13) "out of memory" refcount(5)
}

undefined is returned when there is no matching result.

JSON output

Calling json_encode() on a Zig error object would yield a string formatted in a manner commonly used by web applications:

<?php

$m = zigar_use(__DIR__ . '/error-set-example-1.zig');

try {
    $m->fail(3);
} catch (Exception $e) {
    echo json_encode($e), "\n";
}
{"error":"file not found"}

This string, once parsed, can be casted back into an error object:

<?php

$m = zigar_use(__DIR__ . '/error-set-example-1.zig');

$obj = json_decode('{"error":"file not found"}');
$e = $m->FileOpenError($obj);
echo "$e\n";
ZigException: file not found in :0
Stack trace:
#0 {main}

anyerror

anyerror is a special error set in Zig that contains all errors in a given library. Exporting it would give you a mean to access all public errors:

pub const FileOpenError = error{
    AccessDenied,
    OutOfMemory,
    FileNotFound,
};

pub const HumanError = error{
    GotIntoCryptoCurrencies,
    RanOutOfBeer,
    DidNotKnowHowToUseACondom,
    HungOutWithCliffordBanes,
};

pub const AnyError = anyerror;
<?php

$m = zigar_use(__DIR__ . '/error-set-example-2.zig');

foreach ($m->AnyError as $name => $error) {
    echo "$error\n";
}
access denied
out of memory
file not found
got into crypto currencies
ran out of beer
did not know how to use a condom
hung out with clifford banes

Note that making anyerror public does not make all errors public. If you have a function that explicitly returns an error union with anyerror, it's possible that Zigar would receive an error number that it knows nothing about:

const PrivateError = error{JustBeingEvil};

pub fn fail() anyerror!bool {
    return PrivateError.JustBeingEvil;
}
<?php

$m = zigar_use(__DIR__ . '/error-set-example-3.zig');

try {
    $m->fail();
} catch (Exception $e) {
    echo $e->getMessage(), "\n";
}
unknown error #167

If you simply leave out the error set type and let the compiler infer the type, that type would be made public implicitly and thus visible to Zigar:

const PrivateError = error{JustBeingEvil};

pub fn fail() !bool {
    return PrivateError.JustBeingEvil;
}
<?php

$m = zigar_use(__DIR__ . '/error-set-example-4.zig');

try {
    $m->fail();
} catch (Exception $e) {
    echo $e->getMessage(), "\n";
}
just being evil

Types | Error union