Defining an Interface

  interface Animal {
    fn name(*const self): []const u8;
    fn sound(*const self): []const u8;
    fn legs(*const self): u32;
}
  

Implementing an Interface

  type Dog = struct {
    name_: []const u8,
};

type Cat = struct {
    name_: []const u8,
};

provide Animal for Dog {
    fn name(*const self):  []const u8 { return self.name_; }
    fn sound(*const self): []const u8 { return "woof"; }
    fn legs(*const self):  u32        { return 4; }
}

provide Animal for Cat {
    fn name(*const self):  []const u8 { return self.name_; }
    fn sound(*const self): []const u8 { return "meow"; }
    fn legs(*const self):  u32        { return 4; }
}
  

Static Dispatch — Generics

  fn describe[A: Animal](animal: *const A) {
    debug.print("{} says {} and has {} legs\n",
        .{animal.name(), animal.sound(), animal.legs()});
}

var dog = Dog { .name_ = "Rex" };
var cat = Cat { .name_ = "Whiskers" };

describe(&dog);    -- monomorphized for Dog
describe(&cat);    -- monomorphized for Cat
  

Dynamic Dispatch — dyn

When you need a collection of different types:

  dyn interface Animal {
    fn name(*const self):  []const u8;
    fn sound(*const self): []const u8;
}

fn describeAll(animals: [](*dyn Animal)) {
    for a in animals {
        debug.print("{} says {}\n", .{a.*.name(), a.*.sound()});
    }
}

var dog = Dog { .name_ = "Rex" };
var cat = Cat { .name_ = "Whiskers" };
var animals = []*dyn Animal{ &dog, &cat };
describeAll(&animals);
  

Interface Requirements

Interfaces can require other interfaces:

  interface Printable {
    fn print(*const self, writer: *Writer): !void;
}

interface Saveable requires Printable {
    fn save(*const self, path: []const u8): !void;
}

-- Saveable: must implement both Printable and Saveable
  

Operator Overloading

  operator (+) = Add;
operator (*) = Mul;
operator (=) = Eq;

interface Add[T] {
    fn add(a: T, b: T): T;
}

type Vec2 = struct { x: f32, y: f32 };

provide Add[Vec2] for Vec2 {
    fn add(a: Vec2, b: Vec2): Vec2 {
        return .{ .x = a.x + b.x, .y = a.y + b.y };
    }
}

var a = Vec2 { .x = 1.0, .y = 2.0 };
var b = Vec2 { .x = 3.0, .y = 4.0 };
var c = a + b;    -- Vec2 { .x = 4.0, .y = 6.0 }
  

The Index Interface

  -- arr[i] desugars to arr.getIndex(i)?
-- unsafe { arr.getIndexUnchecked(i) } for unchecked access

provide Index for MyArray {
    type Value = i32;
    type Idx   = usize;

    fn getIndex(*const self, i: usize): !*const i32 {
        if i >= self.len { return error.OutOfBounds; }
        unsafe { return self.getIndexUnchecked(i); }
    }

    unsafe fn getIndexUnchecked(*const self, i: usize): *const i32 {
        unsafe { return #ptrAdd(self.ptr, i); }
    }

    fn setIndex(*self, i: usize, val: i32): !void {
        if i >= self.len { return error.OutOfBounds; }
        unsafe { self.setIndexUnchecked(i, val); }
    }

    unsafe fn setIndexUnchecked(*self, i: usize, val: i32): void {
        unsafe { #ptrAdd(self.ptr, i).* := val; }
    }
}