Structs can be embedded into other structs which lets you access the sub-structs fields directly when accessing them. This allows you to compose structs based on common fields and allow for code re-use.
use fmt;
type entity_kind = enum {
PLAYER,
GOBLIN,
GREMLIN,
};
// Here we define a group of common properties every entity will have.
type entity = struct {
kind: entity_kind,
pos: (int, int),
};
// A player is has all the struct fields an entity has plus a username.
type player = struct {
entity,
username: str,
};
// An enemy has all the struct fields an entity has like a player does,
// but instead, it has an hp field.
type enemy = struct {
entity,
hp: uint,
};
// This function can make use of the fields common to all entities without
// knowing which specific kind of entity is being used.
fn draw_entity(e: *entity) void = {
const kind = switch (e.kind) {
case entity_kind::PLAYER =>
yield "player";
case entity_kind::GOBLIN =>
yield "goblin";
case entity_kind::GREMLIN =>
yield "gremlin";
};
fmt::printfln("There's a {} at ({}, {})!",
kind, e.pos.0, e.pos.1)!;
};
export fn main() void = {
// When initialzing a player we can access the entity fields
// directly since they have been embedded into the player
// struct.
const p1 = player {
kind = entity_kind::PLAYER,
pos = (2, 3),
username = "guts",
};
fmt::printf("p1 ({}, {}) {}\n", p1.pos.0, p1.pos.1, p1.username)!;
// The same goes for the enemy.
const e1 = enemy {
kind = entity_kind::GOBLIN,
pos = (10, 10),
hp = 1337,
};
fmt::printf("e1 ({}, {}) {}\n", e1.pos.0, e1.pos.1, e1.hp)!;
// Pointers to any subtype of entity can be used as *entity:
draw_entity(&p1);
draw_entity(&e1);
};
$ hare run struct-sub-typing.ha
4/4 tasks completed (100%)
p1 (2, 3) guts
e1 (10, 10) 1337
There's a player at (2, 3)!
There's a goblin at (10, 10)!
Back to table of contents