Lesson 8 of 15

Structs

Grouping Data with Structs

Structs are Zig's primary tool for defining custom composite types. They group related data together and can have methods attached to them. If you are coming from C, Zig structs will feel familiar but with significant improvements. If you are coming from an object-oriented language, structs replace classes but without inheritance.

Defining a Struct

const Point = struct {
    x: f64,
    y: f64,
};

Note the trailing comma after the last field. Zig encourages this style because it makes diffs cleaner when you add or remove fields.

Creating Instances

const p = Point{ .x = 3.0, .y = 4.0 };

Fields are always initialized with the . prefix syntax. There is no positional initialization: you must name every field. This is a deliberate design choice that keeps code readable as structs evolve.

Every starship needs a well-defined blueprint before it leaves the shipyard. A Zig struct is no different --- every field must be accounted for, or the compiler refuses to let it launch.

Default Values

Fields can have default values. Any field with a default can be omitted when constructing the struct:

const Config = struct {
    width: u32 = 800,
    height: u32 = 600,
    fullscreen: bool = false,
};

const c = Config{ .fullscreen = true };
// c.width is 800, c.height is 600

Methods

Methods in Zig are functions declared inside a struct that take self (or *self for mutation) as their first parameter:

const Point = struct {
    x: f64,
    y: f64,

    fn distanceFromOrigin(self: Point) f64 {
        return @sqrt(self.x * self.x + self.y * self.y);
    }

    fn translate(self: *Point, dx: f64, dy: f64) void {
        self.x += dx;
        self.y += dy;
    }
};

Call methods with dot syntax:

var p = Point{ .x = 3.0, .y = 4.0 };
const d = p.distanceFromOrigin(); // 5.0
p.translate(1.0, 1.0);           // p is now {4.0, 5.0}

When the method does not need to modify the struct, use self: Point (value). When it does, use self: *Point (pointer).

Namespace Functions

Structs can also contain functions that do not take self. These act as namespace-scoped functions, similar to static methods:

const Point = struct {
    x: f64,
    y: f64,

    fn origin() Point {
        return Point{ .x = 0, .y = 0 };
    }
};

const p = Point.origin();

Packed Structs

Zig also supports packed struct when you need exact control over memory layout, such as when mapping hardware registers or binary protocols:

const Flags = packed struct {
    read: bool,
    write: bool,
    execute: bool,
    _padding: u5 = 0,
};

A packed struct has no padding between fields and guarantees a specific bit layout.

Your Task

Define a Rectangle struct with fields width and height (both f64).

Add two methods:

  • area(self: Rectangle) f64 --- returns the area (width times height).
  • scale(self: *Rectangle, factor: f64) void --- multiplies both width and height by the given factor.
Zig runtime loading...
Loading...
Click "Run" to execute your code.