First of all, I should mention that adding a snippet to expand .. to .{$0} basically fixed the issue for me. I no longer mind typing .{}, and I actually like reading .{}, that’s nice syntactic sign-post.
The reason why we need the leading . and don’t type just { .foo = bar } is syntactic ambiguity. Consider
{ while(false){} }
.{ while(false){} }
The first one is a block expression with a while statement, the second is a tuple with void element. If we remove the leading ., then we’ll need some non-syntactic way to disambiguate the to.
Even { .foo = bar } is syntactically ambiguous, because the left hand size of = can be an expression, and .foo is an expression.
For completeness, {} vs .{} is another ambiguity here (instance of void type vs empty tuple), though arguably void can be an alias for struct {}.
These are not insurmountable difficulties, you can imagine adding enough heuristics to the parser to make it feel right basically 100% in practice, but that’s not the Zig way.
The reason for removing T{} is that:
- It is redundant and doesn’t fit with the rest of the language
T[_]{} special case
- RLS
Note how most languages have suffixes for integer literals (42ull, 92u64), but Zig doesn’t, and result type semantics (const x: u64 = 92) plays the equivalent role. .{} coheres better than T{} with results types, so, in Zig, if you want to keep only one syntax, you should prefer .{}.
The array length inference syntax, u32[_]{ 1, 2, 3 } is problematic because it’s a special case. u32[_] isn’t a type, it’s a separate grammatical category, which can be removed.
Finally, imo the strongest positive case for removal of T{} what that it neutralized RLS:
xs[0] = .{ .foo = 92 } constructs the struct in-place, xs[0] = Foo{ .foo = 92 } does a copy semantically. But now RLS is going away! I am immeasurably sad about that, but I also see how you can’t make that feature usable without something like a borrow checker.