Protocols

A protocol defines a collection of methods that are implemented by multiple types:

proto DataSink {
    func write(self, data: Slice[u8]) -> usize;
    func flush(self);
}

A struct can implement a protocol by defining the required methods. In the following example, both Keyboard and Gamepad implement the InputDevice protocol:

proto InputDevice {
    func name(self) -> *u8;
    func move_direction(self) -> (f32, f32);
}

struct Keyboard: InputDevice {
    var w_pressed: bool;
    var a_pressed: bool;
    var s_pressed: bool;
    var d_pressed: bool;

    func name(self) -> *u8 {
        return "Keyboard";
    }

    func move_direction(self) -> (f32, f32) {
        var direction = (0.0, 0.0);

        if self.d_pressed { direction.0 += 1.0; }
        if self.a_pressed { direction.0 -= 1.0; }
        if self.s_pressed { direction.1 += 1.0; }
        if self.w_pressed { direction.1 -= 1.0; }
        
        return direction;
    }
}

struct Gamepad: InputDevice {
    var name: *u8;
    var axis_x: f32;
    var axis_y: f32;

    func name(self) -> *u8 {
        return self.name;
    }

    func move_direction(self) -> (f32, f32) {
        return (self.axis_x, self.axis_y);
    }
}

Protocols are used to write functions that work with types that share the same behaviour. A pointer to a concrete type can be coerced to a pointer to a protocol:

func main() {
    var gamepad = Gamepad {
        name: "USB Controller",
        axis_x: -0.5,
        axis_y: 0.8
    };

    var device: *InputDevice = &gamepad;
    println(device.name());  # "USB Controller"
}

We can now create a function that works with any kind of input device:

func print_direction(device: *InputDevice) {
    print(device.move_direction().0);
    print(", ");
    println(device.move_direction().1);
}

func main() {
    var keyboard = Keyboard {
        w_pressed: true,
        a_pressed: false,
        s_pressed: false,
        d_pressed: true,
    };

    var gamepad = Gamepad {
        name: "Wireless Controller",
        axis_x: 0.2,
        axis_y: -0.9
    };

    print_direction(&keyboard);  # 1, -1
    print_direction(&gamepad);  # 0.2, -0.9
}