Hacker News new | ask | show | jobs
by coxmi 32 days ago
The dot syntax used everywhere really confuses me. I get its use in struct fields, or for defining anonymous structs, but what is this one for? (Some kind of module-level enum space, where .sampler and .unknown are defined previously?)

  const Sampler = @SpirvType(.sampler);
                             ^

  const Image = @SpirvType(.{ .image = .{
    .usage = .{ .sampled = u32 },
    .format = .unknown,
              ^
  } });
Everything else about zig is quite readable, but this gets me every time. Maybe I'm being dumb though.
1 comments

"Dot" in zig is a placeholder for types that can be unambigously inferred from the surrounding expression.

So ".unknown" is a standin for "SomeEnum.unknown" or "SomeStruct.unknown", depending on what .format is.

Yeah, after looking it up, it looks like it is basically only used as either field access or an 'infer operator', is that right?

I thought it was used in four completely separate ways:

· normal struct field access

· anonymous struct definition

· field definition within structs (for reasons to do with the parser)

· an extra 'infer operator' for syntactic sugar

But there's no support for anonymous structs/fields, and all structs and fields require a type somewhere for it to be inferred. Which is why this is invalid zig:

  const test = .{ .x = 0, .y = 1 };
(It would need the type to be specified in the called function definition, or inline when assigning)

Correct me if I'm wrong here! (And thank you)

I don't think that "infer operator" is a special case of field access, to me it feels like regular known-type elision similar to how C# and C++ use the var keyword if the data type can be inferred from the rhs expression:

    const Enum = enum {one, two, five};
    const t: Enum = .one;  // Enum.one, but the type was inferred from lhs
    std.debug.print("{t}\n", .{t});

Defining an anonymous struct is valid in zig; your example is only invalid because "test" is a reserved keyword. But you are correct that it reifies into a concrete type, and after initialization it doesn't coerce into other types because zig doesn't do structural typing:

    const anonymous = .{ .x = 0, .y = 1 };
    std.debug.print("{}\n", .{@TypeOf(anonymous)}); // will output something like test_0__struct_45138

    const Point = struct { x: i32, y: i32 };
    const p1 = Point{ .x = 0, .y = 1 };  // valid, explicit struct literal
    const p2: Point = .{ .x = 0, .y = 1 };  // valid, anonymous struct will coerce to Point
    //const pt: Point = anonymous; // error: expected type 'test_0.Point', found 'test_0__struct_45138'

And then there's fieldless anonymous structs aka tuples. I'm including them because they were used in the print statements above:

    const tuple = .{ 0, "1", true };
    std.debug.print("{}\n", .{@TypeOf(tuple)});
    // struct { comptime comptime_int = 0, comptime *const [1:0]u8 = "1", comptime bool = true }
Thanks for the detailed answer :)

All this does for me is raise the question of why they chose to use the `.` for so many different uses. I'd be fine if it was just to infer the type, but it seems very overloaded.