Cinder by Example

Cinder is small programming language for low-level development.

Structs

A struct groups named, typed fields.

use "std/io.cnd";

struct Point {
    x: i32;
    y: i32;
}

fn main() -> i32 {
    let p = Point { x: 3, y: 4 };

    let x = p.x;
    print(&x, .I32);

    let sep: []u8 = ", ";
    print(&sep, .S);

    let y = p.y;
    print(&y, .I32);
    putchar(10);

    return 0;
}
$ ./structs
3, 4

Mutating a field

The binding must be mut, and the field must be assigned, not the whole

struct.

use "std/io.cnd";

struct Counter {
    value: i32;
    step: i32;
}

fn main() -> i32 {
    let mut c = Counter { value: 0, step: 2 };
    c.value += c.step;
    c.value += c.step;

    print(&c.value, .I32);
    putchar(10);
    return 0;
}
$ ./mut_struct
4

Functions on structs

Cinder has no methods; a function takes a pointer to the struct. This is the

usual "method" pattern.

use "std/io.cnd";

struct Rect {
    w: i32;
    h: i32;
}

fn area(r: *Rect) -> i32 {
    return r.w * r.h;
}

fn grow(r: *Rect, k: i32) {
    r.w *= k;
    r.h *= k;
}

fn main() -> i32 {
    let mut box = Rect { w: 3, h: 4 };

    let a1 = area(&box);
    print(&a1, .I32);
    putchar(10);

    grow(&box, 2);

    let a2 = area(&box);
    print(&a2, .I32);
    putchar(10);

    return 0;
}
$ ./methods
12
48

Field access through a pointer is automatic: r.w reads through r.

Sized, aligned, offset

sizeof(T), alignof(T), and offsetof(T, field) are compile-time

constants. They match the C ABI of the target.

use "std/io.cnd";

struct Pair {
    a: i32;
    b: u8;
}

static_assert(sizeof(Pair) == 8);
static_assert(alignof(Pair) == 4);
static_assert(offsetof(Pair, a) == 0);
static_assert(offsetof(Pair, b) == 4);

fn main() -> i32 {
    let sz: u64 = sizeof(Pair) as u64;
    print(&sz, .U64);

    let sp: []u8 = " ";
    print(&sp, .S);

    let off: u64 = offsetof(Pair, b) as u64;
    print(&off, .U64);
    putchar(10);

    return 0;
}
$ ./layout
8 4

See Compile-Time Code for more on static_assert.