What does "struct { .x }" mean and why won't @typeInfo compile?

(copied from `struct { .x }` gives an error: expected type 'type', found '@TypeOf(.enum_literal)' · Issue #16418 · ziglang/zig · GitHub)

Code piece 1 (compiles okay):

const std = @import("std");

pub fn main() !void {
    const t = struct { .x };
    std.debug.print("{}\n", .{@TypeOf(t)});
    std.debug.print("{}\n", .{t});
}

What does struct { .x } mean here? It looks to me an illness code.

I have some difficulties to understand the following error messages.

Code piece 2::

pub fn main() !void {
    const t = struct { .x };
    _ = @typeInfo(t);
}

Error message:

error: expected type ‘type’, found ‘@TypeOf(.enum_literal)’
const t = struct { .x };

Code piece 3:

pub fn main() !void {
    const t = struct {
        x: u32,
    };
    std.debug.print("{}\n", .{@typeInfo(t)});
}

Error message:

/zig/lib/std/fmt.zig:662:22: error: values of type ‘[]const builtin.Type.StructField’ must be comptime-known, but index value is runtime-known
for (value, 0…) |elem, i| {
^~~~~
/zig/lib/std/builtin.zig:318:15: note: struct requires comptime because of this field
type: type,
^~~~
/zig/lib/std/builtin.zig:318:15: note: types are not available at runtime
type: type,
^~~~
/zig/lib/std/builtin.zig:321:20: note: struct requires comptime because of this field
alignment: comptime_int,
^~~~~~~~~~~~

struct { .x }

is a tuple with a single field “.x” which is an enum literal.

You’re defining a struct here which has 1 indexed value which is of type .x. This doesn’t make much sense though, you’d probably mean to do something like:

const MyEnum = enum { e1, e2 };

pub fn main() !void {
    const t = struct { MyEnum };
    const y: t = .{ .e1 };

    std.debug.print("{}\n", .{y});
    std.debug.print("{}\n", .{y[0]});
}

With t I’m defining the struct and with y I’m using the struct I defined which is a single value anonymous struct whose value type is a variation of MyEnum. You’ll notice it’s indexable as well. You can also do something like this:

const MyEnum = enum { e1, e2 };

pub fn main() !void {
    const t = struct { MyEnum, u8 };
    const y: t = .{ .e1, 12 };

    std.debug.print("{}\n", .{y});
    std.debug.print("{}\n", .{y[0]});
    std.debug.print("{}\n", .{y[1]});
}

Now it’s a 2 valued struct with index 0 being a variation of MyEnum and index 1 being a u8. It’s effectively a multi-type array.

You’re other 2 code pieces will work if you do the following:

code piece 2:

pub fn main() !void {
    const t = struct { .x };
    _ = @typeInfo(@TypeOf(t));
}

code piece 3:

pub fn main() !void {
    const t = struct {
        x: u32,
    };
    std.debug.print("{}\n", .{@typeInfo(@TypeOf(t))});
}

You were trying to call @typeInfo with a struct when it takes a type

6 Likes

Also, I edited you’re title from

Newbie questions: what does code piece 1 compiles, but piece 2 and 3 doesn’t

to

What does “struct { .x }” mean and why won’t @typeInfo compile?

You’ll get more responses sooner if people can get a general sense of what the topic is about before clicking on it

Hope this helps!

3 Likes

Many thanks for the clarifications. However, I’m still confused at some points.

  1. What’s the principle difference between u8 and struct{x: u8,} in the following code. Aren’t they both two general types? We can’t get struct type info?

The error message for the second @typeInfo(t) does hint it is related to struct field, but the message is too hard for new zig programmers to understand.

pub fn main() !void {
    {
		const t = u8;
		std.debug.print("{}\n", .{ t});
		std.debug.print("{}\n", .{ @TypeOf(t) });
		std.debug.print("{}\n", .{ @typeInfo(t) });
		
		// u8
		// type
		// builtin.Type{ .Int = builtin.Type.Int{ .signedness = builtin.Signedness.unsigned, .bits = 8 } }
	}
	std.debug.print("\n", .{});
	{
		const t = struct{x: u8,};
		std.debug.print("{}\n", .{ t});
		std.debug.print("{}\n", .{ @TypeOf(t) });
		//std.debug.print("{}\n", .{ @typeInfo(t) }); // not compile, with hard-to-understand error message
		
		// y.main.t
		// type
	}
}
  1. What the use cases of the x, y and z declared in the following code?
pub fn main() !void {
    const x = .foo;
    _ = x;
    const y = struct{.foo};
    _ = y;
    const z = .{.foo};
    _ = z;
}

Part of the issue here as that std.debug.print has some known oddities around printing compile time information. You’ll notice that:

@compileLog(@typeInfo(t)); 

prints:

@as(builtin.Type, .{ .Struct = .{.layout = .Auto, .backing_integer = null, .fields = .{ .{ … } }, .decls = .{ }, .is_tuple = false} })

There is a known issue with the compile time use of std.debug.print giving confusing messages:

If we walk through your original error message, we’ll see that it’s having an issue using the information you provided as it’s attempting to gather that info at runtime:

/zig/lib/std/fmt.zig:662:22: error: values of type ‘[]const builtin.Type.StructField’ must be comptime-known, but index value is runtime-known
for (value, 0…) |elem, i| {
^~~~~

This error is saying that it can’t iterate through the struct fields at compile time because because…

/zig/lib/std/builtin.zig:318:15: note: struct requires comptime because of this field
type: type,
^~~~

…one of the arguments (in the form of foo(comptime T: type)) is being evaluated at runtime.

The issue with this code:

std.debug.print("{}\n", .{ @typeInfo(@TypeOf(t)) });

Is that it prints the following:

builtin.Type{ .Type = void }

You’ll notice the “.Type = void”. Here’s why:

@compileLog(@typeInfo(@TypeOf(t)));

Will print the exact same thing:

@as(builtin.Type, .{ .Type = void })

This is because we’re calling the @TypeOf function on something that is already a type. The “type of” a “type” is just Builtin.Type.

Essentially, it looks like std.debug.print is having a hard time evaluating this at compile time. In the first case where you are printing a single u8, it looks like we can do that because we know what the offset is and it returns the correct thing. In a more generic case (a struct with a variable number of members) it’s struggling to return something about that in the runtime context.

This may be a bug (I submitted something regarding standard IO a while ago with the “zig test” feature because IO is tricky there as well). That github link I attached is evidence that this is causing some confusion and has been accepted because there is a pain-point here.

I think @zraineri covered everything else here. Looks like it’s just an issue with how we handle printing comptime stuff in the current version of Zig.

4 Likes

Okay, thanks for the explanations.

My confusions are still not totally cleared. Maybe I need some time to comprehend some zig corners eventually.

It is a sad fact that we can’t get type info of general struct types (It would be a good idea to mention the limit of @typeInfo and the @compileLog workaround in the doc of @typeInfo).