An anonymous struct is a struct type without a name.
You have already seen named structs:
const Point = struct {
x: i32,
y: i32,
};Here, the struct type has a name: Point.
An anonymous struct does not have a declared name:
const p = .{
.x = 10,
.y = 20,
};This creates a value with two fields, x and y, but we did not define a separate Point type.
The type exists, but it is inferred from the literal.
Anonymous Struct Literals
The basic anonymous struct literal looks like this:
.{
.field_name = value,
.field_name = value,
}Example:
const user = .{
.id = 1,
.name = "Ada",
.active = true,
};This value has three fields:
id
name
activeYou can access them with dot syntax:
const name = user.name;
const active = user.active;This works like a normal struct value.
Named Struct vs Anonymous Struct
A named struct is better when the data has a clear meaning in your program.
const User = struct {
id: u64,
name: []const u8,
active: bool,
};Then you create values like this:
const user = User{
.id = 1,
.name = "Ada",
.active = true,
};An anonymous struct is better for small local values:
const user = .{
.id = 1,
.name = "Ada",
.active = true,
};The difference is not only style. A named struct gives the type a reusable name. An anonymous struct does not.
Use a named struct when the type appears in function signatures, public APIs, collections, or important program logic.
Use an anonymous struct when the value is short-lived and local.
Anonymous Structs in Formatting
You often see anonymous structs in std.debug.print.
std.debug.print("x = {}, y = {}\n", .{ 10, 20 });The second argument is an anonymous tuple-like struct:
.{ 10, 20 }It has values but no field names.
This is how Zig passes formatting arguments. The format string has two {} placeholders, and the anonymous struct contains two values.
std.debug.print("{} + {} = {}\n", .{ 2, 3, 5 });Output:
2 + 3 = 5This is one of the most common places beginners first see anonymous structs.
Tuple-Like Anonymous Structs
Anonymous structs can be written without field names:
const pair = .{ 10, "hello" };This is a tuple-like anonymous struct.
The fields are indexed by position. You can access them with numeric field names:
const number = pair[0];
const text = pair[1];In Zig, tuple-like structs are useful for temporary groups of values, especially when passing arguments to formatting functions.
But for most program data, prefer named fields:
const pair = .{
.count = 10,
.message = "hello",
};This is easier to read:
pair.count
pair.messageField names explain meaning. Positions require the reader to remember meaning.
Anonymous Structs with Field Names
You can use named fields without declaring a named type:
const config = .{
.host = "127.0.0.1",
.port = 8080,
.debug = false,
};Then:
std.debug.print("{}:{}\n", .{ config.host, config.port });This is useful when the value is only needed in one small area.
For example:
const server = .{
.host = "127.0.0.1",
.port = 8080,
};
std.debug.print("server = {s}:{}\n", .{
server.host,
server.port,
});The anonymous struct keeps the code compact without creating a new top-level type.
Anonymous Structs and Type Inference
This expression:
const p = .{
.x = 10,
.y = 20,
};uses type inference.
Zig sees the fields and values, then creates a matching anonymous struct type.
The field x gets the type of 10.
The field y gets the type of 20.
Sometimes you want a specific named type. In that case, write the type explicitly:
const Point = struct {
x: i32,
y: i32,
};
const p = Point{
.x = 10,
.y = 20,
};This makes p a Point.
Coercing Anonymous Structs to Named Structs
An anonymous struct literal can often be used where a named struct is expected.
const Point = struct {
x: i32,
y: i32,
};
fn printPoint(p: Point) void {
std.debug.print("({}, {})\n", .{ p.x, p.y });
}
pub fn main() void {
printPoint(.{
.x = 10,
.y = 20,
});
}The function expects a Point.
The call passes an anonymous struct literal with the right fields.
Zig can coerce it into Point.
This is common and useful. You do not always need to write Point{ ... } if the expected type is already clear from context.
You can also write it explicitly:
printPoint(Point{
.x = 10,
.y = 20,
});Both forms are valid when the context is clear.
Anonymous Structs in Function Calls
Anonymous struct literals are useful for options parameters.
Suppose a function accepts a config struct:
const Options = struct {
verbose: bool = false,
retries: u8 = 3,
};
fn run(options: Options) void {
_ = options;
}You can call it like this:
run(.{});This uses all defaults.
You can override one field:
run(.{
.verbose = true,
});The expected type is Options, so Zig knows how to interpret the literal.
This style keeps call sites short while preserving type checking.
Anonymous Structs Are Still Strongly Typed
Anonymous does not mean dynamic.
This is still compile-time checked:
const value = .{
.name = "Ada",
.age = 36,
};The fields are known at compile time.
This works:
const age = value.age;This does not:
const email = value.email;There is no email field, so Zig rejects the program.
Anonymous structs are not maps. They are not dictionaries. They are fixed compile-time types.
Anonymous Structs Are Not Good Public APIs
Avoid anonymous structs in public interfaces when the data has an important meaning.
This is harder to understand:
fn createUser(data: anytype) void {
_ = data.name;
_ = data.email;
}The caller has to guess what shape data should have.
This is clearer:
const CreateUserOptions = struct {
name: []const u8,
email: []const u8,
active: bool = true,
};
fn createUser(options: CreateUserOptions) void {
_ = options.name;
_ = options.email;
}Now the type documents the required fields.
Anonymous structs are best for local convenience, not for hiding important structure.
Anonymous Structs as Namespaces
You can also create anonymous structs for grouping declarations, though named structs are usually clearer.
const Math = struct {
pub fn add(a: i32, b: i32) i32 {
return a + b;
}
pub fn sub(a: i32, b: i32) i32 {
return a - b;
}
};Technically, the struct { ... } type is anonymous. It gets the name Math because we assign it to a constant.
This pattern is common in Zig. Many named types are written this way:
const Name = struct {
// fields and declarations
};The struct expression itself is anonymous. The constant gives it a useful name.
A Practical Example
Here is a small program that uses both named and anonymous structs:
const std = @import("std");
const Server = struct {
host: []const u8,
port: u16,
pub fn print(self: Server) void {
std.debug.print("server = {s}:{}\n", .{
self.host,
self.port,
});
}
};
pub fn main() void {
const local = Server{
.host = "127.0.0.1",
.port = 8080,
};
local.print();
const temporary = .{
.host = "example.com",
.port = 443,
};
std.debug.print("temporary = {s}:{}\n", .{
temporary.host,
temporary.port,
});
}Server is a named struct because it represents a real concept in the program.
temporary is anonymous because it is only used once.
The Main Idea
Anonymous structs let you group values without creating a named type.
const p = .{
.x = 10,
.y = 20,
};They are useful for formatting arguments, short local values, and option literals where the expected type is already clear.
Use named structs for important concepts:
const Point = struct {
x: i32,
y: i32,
};Use anonymous structs for small temporary groups:
.{ 10, 20 }The rule is simple: if the shape matters across your program, give it a name.