Djinn Lang

Generics

Generic Structs

Structs can be parameterized with one or more type parameters:

struct Box<T> {
    T value;

    Box(T v) {
        this.value = v;
    }

    public T get() {
        return this.value;
    }
}

i32 main() {
    Box<i32> b = Box<i32>(42);
    return b.get();  // 42
}

Multiple Type Parameters

struct Pair<K, V> {
    K key;
    V value;
}

i32 main() {
    Pair<i32, i32> p = { .key = 42, .value = 100 };
    return p.key + p.value;  // 142
}

Monomorphization

Generics are monomorphized at compile time. Each unique instantiation (e.g., Box<i32>, Box<i64>) generates a separate struct and method set. There is no runtime cost.

Generic Enums

Enums also support generics:

enum optional<T> {
    Empty(),
    Value(T)
}

optional<i32> x = optional<i32>::Value(42);

See Enums for more details.

Generic Constraints

Type parameters can be constrained with interfaces using where:

interface IValue {
    i32 getValue();
}

struct Container<T> where T : IValue {
    T item;
}

Only types that implement IValue can be used as T. Types that don't satisfy the constraint produce a compile error:

struct MyItem : IValue {
    i32 data;
    i32 getValue() { return this.data; }
}

struct Plain { i32 x; }

Container<MyItem> ok = { .item = { .data = 42 } };   // ok
Container<Plain> err = { .item = { .x = 1 } };        // error: Plain does not implement IValue

Multiple Constraints

A type parameter can require multiple interfaces:

interface IReadable {
    i32 read();
}

interface IWritable {
    void write(i32 val);
}

struct Store<T> where T : IReadable, IWritable {
    T backend;
}

T must implement both IReadable and IWritable. Implementing only one produces a compile error.

Constraints on Multiple Parameters

Use separate where clauses joined by ;:

interface IHashable {
    i32 hash();
}

interface ISerializable {
    i32 serialize();
}

struct Map<K, V> where K : IHashable; where V : ISerializable {
    K key;
    V value;
}

Constraints on Enums

where clauses work on enums too:

interface IValue {
    i32 getValue();
}

enum Wrapper<T> where T : IValue {
    Some(T),
    None()
}

Constraints on Interfaces

Generic interfaces can constrain their own type parameters:

interface IEquatable {
    i32 equals();
}

interface ISortable<T> where T : IEquatable {
    void sort();
}

Constraints with Implements

A struct can have both a where clause on its generic parameters and implement interfaces:

interface IContainer {
    i32 size();
}

interface IValue {
    i32 getValue();
}

struct TypedBox<T> where T : IValue : IContainer {
    T item;
    i32 count;

    i32 size() { return this.count; }
}

Nested Generics

Generic types can be nested as type arguments:

struct Pair<K, V> {
    K key;
    V value;
}

struct Wrapper<T> {
    T data;
    i32 id;
}

i32 main() {
    Wrapper<Pair<i32, i32>> w;
    return 0;
}

Generic Return Types

Functions can return generic structs:

struct Pair<K, V> {
    K key;
    V value;
}

Pair<i32, i32> makePair(i32 k, i32 v) {
    return { .key = k, .value = v };
}

i32 main() {
    auto p = makePair(5, 10);
    return p.key + p.value;  // 15
}

On this page