# Struct Methods

### Struct Methods

A struct method is a function that belongs to a struct.

Start with a normal struct:

```zig
const Point = struct {
    x: i32,
    y: i32,
};
```

Now add a function inside it:

```zig
const Point = struct {
    x: i32,
    y: i32,

    fn print(self: Point) void {
        std.debug.print("({}, {})\n", .{ self.x, self.y });
    }
};
```

The function `print` is inside `Point`, so you call it through `Point` or through a `Point` value.

```zig
const p = Point{
    .x = 10,
    .y = 20,
};

p.print();
```

This prints:

```text
(10, 20)
```

The important part is this parameter:

```zig
self: Point
```

By convention, Zig uses the name `self` for the value the method works on.

When you write:

```zig
p.print();
```

Zig treats it like:

```zig
Point.print(p);
```

So method syntax is mostly convenient function-call syntax.

#### Methods Are Just Functions

Zig does not have classes. A method is still a function.

This:

```zig
p.print();
```

is shorthand for this:

```zig
Point.print(p);
```

That means Zig keeps the model simple. A struct contains fields and declarations. A function inside a struct is one of those declarations.

There is no hidden `this`. There is no automatic inheritance. There is no object system behind the scenes.

You decide what gets passed.

#### Methods That Read Data

A method that only reads a struct can take `self` by value:

```zig
const Point = struct {
    x: i32,
    y: i32,

    fn sum(self: Point) i32 {
        return self.x + self.y;
    }
};
```

Use it like this:

```zig
const p = Point{
    .x = 3,
    .y = 4,
};

const result = p.sum();
```

Now `result` is `7`.

Passing by value copies the struct. For small structs, this is fine.

For larger structs, use a pointer:

```zig
fn sum(self: *const Point) i32 {
    return self.x + self.y;
}
```

Now the method receives a pointer to the original value, but it promises not to modify it.

The `*const Point` type means “pointer to a Point that I will not change.”

#### Methods That Change Data

A method that changes the struct should take a mutable pointer:

```zig
const Counter = struct {
    value: i32,

    fn increment(self: *Counter) void {
        self.value += 1;
    }
};
```

Use it like this:

```zig
var counter = Counter{
    .value = 0,
};

counter.increment();
counter.increment();
```

After this, `counter.value` is `2`.

The method uses:

```zig
self: *Counter
```

That means it receives a mutable pointer to the original `Counter`.

This matters. Without a pointer, the method would modify only a copy.

#### Value Receiver vs Pointer Receiver

There are three common forms:

```zig
fn method(self: Type) void
```

Use this when the struct is small and the method only needs a copy.

```zig
fn method(self: *const Type) void
```

Use this when the method only reads the value and you want to avoid copying.

```zig
fn method(self: *Type) void
```

Use this when the method needs to modify the value.

Example:

```zig
const Buffer = struct {
    len: usize,

    fn length(self: Buffer) usize {
        return self.len;
    }

    fn lengthNoCopy(self: *const Buffer) usize {
        return self.len;
    }

    fn clear(self: *Buffer) void {
        self.len = 0;
    }
};
```

The receiver type tells the reader what the method can do.

#### Constructor-Like Functions

Zig does not have constructors.

Instead, you usually write a normal function that returns a struct value.

A common name is `init`:

```zig
const User = struct {
    id: u64,
    name: []const u8,

    fn init(id: u64, name: []const u8) User {
        return User{
            .id = id,
            .name = name,
        };
    }
};
```

Use it like this:

```zig
const user = User.init(1, "Ada");
```

This is clear. `User.init` is just a function that returns a `User`.

There is no hidden allocation. There is no automatic setup step. The function does exactly what the code says.

#### Methods Can Return New Values

A method does not always modify the current value. It can return a new value instead.

```zig
const Point = struct {
    x: i32,
    y: i32,

    fn moved(self: Point, dx: i32, dy: i32) Point {
        return Point{
            .x = self.x + dx,
            .y = self.y + dy,
        };
    }
};
```

Use it like this:

```zig
const p1 = Point{
    .x = 10,
    .y = 20,
};

const p2 = p1.moved(5, -3);
```

Now:

```text
p1 is still (10, 20)
p2 is (15, 17)
```

This style is useful when you want immutable values.

#### Methods Can Call Other Methods

Methods can call other methods in the same struct:

```zig
const Rectangle = struct {
    width: u32,
    height: u32,

    fn area(self: Rectangle) u32 {
        return self.width * self.height;
    }

    fn isSquare(self: Rectangle) bool {
        return self.width == self.height;
    }

    fn describe(self: Rectangle) void {
        std.debug.print("area = {}, square = {}\n", .{
            self.area(),
            self.isSquare(),
        });
    }
};
```

Here, `describe` calls `area` and `isSquare`.

```zig
const r = Rectangle{
    .width = 10,
    .height = 20,
};

r.describe();
```

This keeps related behavior near the data.

#### Public and Private Methods

Struct functions can be public or private.

A private function uses `fn`:

```zig
fn helper() void {
    // private
}
```

A public function uses `pub fn`:

```zig
pub fn init() Type {
    // public
}
```

Inside one file, this difference may not seem important. It matters when another file imports your code.

Example:

```zig
const Counter = struct {
    value: i32,

    pub fn init() Counter {
        return Counter{ .value = 0 };
    }

    pub fn increment(self: *Counter) void {
        self.value += 1;
    }

    fn secretHelper(self: Counter) i32 {
        return self.value * 2;
    }
};
```

Code outside this file can call `Counter.init` and `counter.increment`.

It cannot call `secretHelper`.

#### A Complete Example

```zig
const std = @import("std");

const Counter = struct {
    value: i32,

    pub fn init(start: i32) Counter {
        return Counter{
            .value = start,
        };
    }

    pub fn increment(self: *Counter) void {
        self.value += 1;
    }

    pub fn add(self: *Counter, amount: i32) void {
        self.value += amount;
    }

    pub fn get(self: Counter) i32 {
        return self.value;
    }
};

pub fn main() void {
    var counter = Counter.init(10);

    counter.increment();
    counter.add(5);

    std.debug.print("counter = {}\n", .{counter.get()});
}
```

Output:

```text
counter = 16
```

Read the flow:

```zig
var counter = Counter.init(10);
```

Create a counter with value `10`.

```zig
counter.increment();
```

Change it to `11`.

```zig
counter.add(5);
```

Change it to `16`.

```zig
counter.get()
```

Read the final value.

#### Why Methods Matter

Methods keep operations close to the data they operate on.

Instead of writing unrelated functions everywhere, you can group them with the struct:

```zig
const Counter = struct {
    value: i32,

    pub fn increment(self: *Counter) void {
        self.value += 1;
    }
};
```

This makes the program easier to navigate. When you find the `Counter` type, you also find the important operations for `Counter`.

The main rule is simple: put a function inside a struct when the function strongly belongs to that type.

