Beginner Syntax Question: void vs !void

What is the difference between

pub fn main() void {
...
}

and

pub fn main() !void {
...
}

They seem to do the same thing, what’s happening there?

The second one means that the function returns either an error or nothing.

1 Like

What does the first one return?

It does not return anything.

Thanks @dee0xeed! also leaving this if someone else searches my dumb question.

from andrewk on IRC, https://ziglang.org/documentation/master/#Hello-World 

Try running this:

const print = @import("std").debug.print;

fn diff(a: u32, b: u32) !u32 {
    if (b > a)
        return error.NoWay;
    return a - b;
}

pub fn main() !void {
    const x = try diff(7, 5);
    print("x = {}\n", .{x});
    const y = try diff(5, 7);
    print("y = {}\n", .{y});
}

You’ll see

$ /opt/zig-0.12/zig run 1.zig 
x = 2
error: NoWay
/home/user/1.zig:6:9: 0x21d370 in diff (1)
        return error.NoWay;
        ^
/home/user/1.zig:13:15: 0x21d5a4 in main (1)
    const y = try diff(5, 7);
1 Like

And then this:

const print = @import("std").debug.print;

fn diff(a: u32, b: u32) u32 {
    return a - b;
}

pub fn main() void {
    const x = diff(7, 5);
    print("x = {}\n", .{x});
    const y = diff(5, 7);
    print("y = {}\n", .{y});
}

:slight_smile:

1 Like

@xgpt , welcome to Zig!

Almost a year ago, when I was starting my Zig journey, there was a question that was bothering me - I kept seeing @ in many places in Zig examples (most notably in @import) and I didn’t know what it was. I could not find the answer, so I turned to ChatGPT.

Here’s our dialog (needless to say, this is completely wrong, and “c”, “less”, and “arduino” are code section captions):

Explain the use of @ symbol in Zig programming language

ChatGPT:
In the Zig programming language, the “@” symbol is used to indicate a pointer type. When used in a variable declaration, it signifies that the variable is a pointer to a value of the specified type. For example, the following code declares a pointer to an integer:

c

const int *my_int_ptr = @int(0);

It can also be used in function calls to pass a pointer to a value rather than the value itself. For example, the following code passes a pointer to an integer to a function:

less

fn my_function(const int *my_int_ptr) {
    // Do something with my_int_ptr
}

const int my_int = 5;
my_function(@my_int);

It also used in function return types to indicate the function will return a pointer to a value of the specified type.

c

fn* return_int_ptr() void {
    const int *my_int_ptr = @int(0);
    return my_int_ptr;
}

You can also use the @ symbol to dereference a pointer and access the value it points to.

arduino

const int my_int = 5;
const int *my_int_ptr = @my_int;
const int my_int_deref = *my_int_ptr;

It’s also used to take the address of a variable.

arduino

const int my_int = 5;
const int *my_int_ptr = &my_int;

Note that the & symbol is used to take the address of a variable, while the @ symbol is used to indicate a pointer type or dereference a pointer.

I always use anyerror!void to explicitely show that the function can return an error or void.

I do not think this is “idiomatic”.
anyerror is for cases when compiler cannot infer error set
For instance, when declaring a type which is a pointer to function:

const implFnPtr = *const fn(*VirtualMachine) anyerror!void;
3 Likes

An addition: the exclamation sign before return type by itself is already an explicit indication that a function may return an error.

1 Like

IIRC, !<type> means “let the compiler figure out which errors can the function return, based on the called functions and return statements”, whereas anyerror!<type> means “the function can return ANY error”.

I prefer limiting the scope, if i can.

2 Likes

I do not find it very clear and very explicit.

If you want to be maximally explicit just define your own error set:

const FileOpenError = error {
    NotFound,
    PermissionDenied,
    ...
};

fn openFile(...) FileOpenError!... {
    ...
    return PermissionDenied;
    ...
}

When you are lazy, just let compiler infer the set for you:

fn openFile(...) !... {
    ...
    return error.NotFound;
    ...
}
1 Like

It returns a void, a zero-bit type with only one possible value: {}.

A something with zero size can not contain nothing, can it?..

Something containing nothing is still something conceptually. void is better described as “no useful information”. It’s not nothing. null is nothing. A function can return that only when it returns an optional.

n bits may represent 2^n different values. Thus, 0 bits represent 2^0=1 value. This is the case for void. If you know that a variable is of type void, you need 0 bits to determine that it contains value {}.

3 Likes