Initializing void in 0.17

In 0.17 Type{…} syntax to initialize is dropped.
I have many functions lying around that take in void as an argument:

fn foo(_: void) void {}

These usually happen in functions that take in comptime stuff.
The following ways don’t work:

foo(void{}); // Error
foo(.{}); // Error

Instead the only way I found is this:

foo(undefined); // Works, but I hate looking at it
const VOID: void = undefined; //Maybe better
foo(VOID);

Is there a way to initialize void that’s not undefined?

Another use case is either type:

const A: if(myBool) void else T = .{}; // doesn't work

T{ ... } syntax in general isn’t gone yet (though it will be going away), it’s just void{}, because that one was always a weird special case in the language. The canonical way to write a void value is just {} (no dot).

9 Likes

Didn’t know about that one!
What about the either type use case? Do I have to do
const T = if (@TypeOf(T) == void) {} else .{}; for default initialization now?

I ended up replacing void with struct{} in my codebase.

Sure, that works! You could also do:

const x: if (condition) T else void = if (condition) .{} else {};
// or, equivalently, because `else {}` is the "default" so to speak:
const x: if (condition) T else void = if (condition) .{}; 

Also, if you’re writing a struct field with a default value (rather than initializing a const/var), consider whether you should actually be giving it a default value! Most struct fields shouldn’t have default values.

4 Likes

That second thing is black magic to me, I didn’t know you could do that! I do think struct{} over void might have the advantage even with that trick, because it wouldn’t require any checking, myVar = .{}; would always work

For default initialization
When doing a GUI library for the text rendering layout stuff I was in a situation where I speculate the combination of metaprogramming + default initialization probably made the code 3x smaller. I can share the code if needed (will share the whole library when it’s finished).

For the migration:
I don’t think I encountered the second example I provided (or if I did then it was once), where I need default initialization of void because of an either type. That was more trying to figure out the worse case scenario.

The migration was easy in general, at least for that project.

Don’t know which comment to mark as solution so I’ll just mark this

But what’s the point to take void as argument at all?

It happens sometimes because of comptime type parameterization.

Consider a type function like this:

fn Container(comptime T: type, comptime Context: type) type {
    return struct {
        // ...
        fn doSomething(self: *@This(), ctx: Context) void {
            // ...
        }
    };
}

It might so happen that your particular use case doesn’t need a context, so you use Container like so:

var container: Container(u32, void) = .init;

Because Context is now void, its doSomething method takes void as its second parameter:

container.doSomething({});
3 Likes