9. Object Oriented Programming

April 1, 2026 ยท View on GitHub

+++ title = "9. Object Oriented Programming" weight = 9 +++

9. Object Oriented Programming

Methods

Define methods on types using impl.

impl Point {
    // Static method (constructor convention)
    fn new(x: int, y: int) -> Self {
        return Point{x: x, y: y};
    }

    // Instance method
    fn dist(self) -> float {
        return sqrt(self.x * self.x + self.y * self.y);
    }
}

Self Shorthand: In methods with a self parameter, you can use .field as shorthand for self.field:

impl Point {
    fn dist(self) -> float {
        return sqrt(.x * .x + .y * .y);  // Equivalent to self.x, self.y
    }
}

Primitive Methods

Zen C allows you to define methods on primitive types (like int, bool, etc.) using the same impl syntax.

impl int {
    fn abs(self) -> int {
        return *self < 0 ? -(*self) : *self;
    }
}

let x = -10;
let y = x.abs(); // 10
let z = (-5).abs(); // 5 (Literals supported)

Traits

Define shared behavior.

struct Circle { radius: f32; }

trait Drawable {
    fn draw(self);
}

impl Drawable for Circle {
    fn draw(self) { ... }
}

let circle = Circle{};
let drawable: Drawable = &circle;

Standard Traits

Zen C includes standard traits that integrate with language syntax.

Iterable

Implement Iterable<T> to enable for-in loops for your custom types.

import "std/iter.zc"

// Define an Iterator
struct MyIter {
    curr: int;
    stop: int;
}

impl MyIter {
    fn next(self) -> Option<int> {
        if self.curr < self.stop {
            self.curr += 1;
            return Option<int>::Some(self.curr - 1);
        }
        return Option<int>::None();
    }
}

// Implement Iterable
impl MyRange {
    fn iterator(self) -> MyIter {
        return MyIter{curr: self.start, stop: self.end};
    }
}

// Use in Loop
for i in my_range {
    println "{i}";
}

Drop

Implement Drop to define a destructor that runs when the object goes out of scope (RAII).

import "std/mem.zc"

struct Resource {
    ptr: void*;
}

impl Drop for Resource {
    fn drop(self) {
        if self.ptr != NULL {
            free(self.ptr);
        }
    }
}

{% alert(type="important") %} Note: If a variable is moved, drop is NOT called on the original variable. It adheres to Resource Semantics. {% end %}

Copy

Marker trait to opt-in to Copy behavior (implicit duplication) instead of Move semantics. Used via @derive(Copy).

{% alert(type="caution") %} Rule: Types that implement Copy must not define a destructor (Drop). {% end %}

@derive(Copy)
struct Point { x: int; y: int; }

fn main() {
    let p1 = Point{x: 1, y: 2};
    let p2 = p1; // Copied! p1 remains valid.
}

Clone

Implement Clone to allow explicit duplication of resource-owning types.

import "std/mem.zc"

struct MyBox { val: int; }

impl Clone for MyBox {
    fn clone(self) -> MyBox {
        return MyBox{val: self.val};
    }
}

fn main() {
    let b1 = MyBox{val: 42};
    let b2 = b1.clone(); // Explicit copy
}

Composition

Use use to embed other structs. You can either mix them in (flatten fields) or name them (nest fields).

struct Entity { id: int; }

struct Player {
    // Mixin (Unnamed): Flattens fields
    use Entity;  // Adds 'id' to Player directly
    name: string;
}

struct Match {
    // Composition (Named): Nests fields
    use p1: Player; // Accessed via match.p1
    use p2: Player; // Accessed via match.p2
}