TON DocsTON Docs
OnboardingNodesApplicationsAPIsSmart contractsTolkTolk languageTVMTON Virtual MachineFoundationsBlockchain foundations

Generic structs and aliases

Generic structs and type aliases exist only at the type level and incur no runtime cost.

struct Container<T> {
    element: T?
}

struct Nothing

type Wrapper<T> = Nothing | Container<T>

Example:

fun checkElement<T>(c: Container<T>) {
    return c.element != null;
}

fun demo() {
    var c: Container<int32> = { element: null };

    var n: Wrapper<int> = Nothing {};
    var i: Wrapper<int32> = Container { element: 0 };
}

Type arguments

When referencing a generic Container, T must typically be provided:

fun getItem(c: Container)        // error
fun getItem(c: Container<int>)   // ok
fun getItem<T>(c: Container<T>)  // ok

For generic functions, type arguments can be inferred from the call site:

fun doSmth<T>(value: Container<T>) {
    // ...
}

fun demo() {
    doSmth({ element: 123 });         // T = int
    doSmth({ element: cellOrNull });  // T = cell?
}

When calling a method on a generic type, type arguments may be omitted if they can be inferred:

type Maybe<T> = MaybeJust<T> | MaybeNothing

fun Maybe<T>.none(): Maybe<T> {
    return MaybeNothing {}
}

fun demo() {
    // `Maybe.none()` is enough — identical to `Maybe<address>.none()`
    var a_none: Maybe<address> = Maybe.none();
}

Default type arguments

A type parameter can have a default type: <T = xxx>. Such type parameters may be omitted at use sites.

struct StrangeUnion<T1, T2 = null> {
    item: T1 | T2
}

fun demo() {
    var m1: StrangeUnion<int> = { item: 10 };
    var m2: StrangeUnion<int, bool> = { item: 20 };
    // m1.item is `int?`
    // m2.item is `int | bool`
}

If all type parameters have defaults, they can be omitted:

struct ComplexMsg<TBody = int32> {
    // ...
}

fun demo(m: ComplexMsg) {
    // m is `ComplexMsg<int32>`
}

Type arguments cannot reference one another; expressions such as T2 = T1 are invalid.

Type aliases

Generic parameters are supported not only by structs, but also by type aliases. The following example defines a generic type alias Response<TResult, TError>:

struct Ok<TResult> { result: TResult }
struct Err<TError> { err: TError }

type Response<R, E> = Ok<R> | Err<E>

fun loadNextRef(mutate s: slice): Response<cell, int32> {
    return s.remainingRefsCount()
        ? Ok { result: s.loadRef() }
        : Err { err: ERR_NO_MORE_REFS }
}

fun demo() {
    match (val r = loadNextRef(mutate parsedSlice)) {
        Ok => { r.result }    // cell
        Err => { r.err }      // int32
    }
}

Methods

Methods for generics are declared exactly as for regular structures. In this form, the compiler treats T, which is unknown symbol, as a type parameter:

fun Container<T>.getElement(self) {
    return self.element
}

Without self parameter, a method will be static.

On this page