Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.ton.org/llms.txt

Use this file to discover all available pages before exploring further.

Tolk provides the types void and never, which both describe the absence of a value, but with different semantics.

void

A function that returns nothing is void. Both functions below return void:
fun demo1() {
    debug.print(globalVar);
}

fun demo2() {
    if (random.uint256() == 0) {
        return;
    }
    globalVar = 123;
}
When a function return type is not specified, it is inferred from the return statements. A return without a value indicates a void return type. Explicitly specifying fun(...): void behaves identically:
fun demo1(): void {
    // ...
}

void-typed fields

A structure field with type void is treated as non-existent. This pattern is used with generics such as <T = void> to model optional fields in structure types.
struct WithOptionalField<T> {
    a: int
    b: T
    c: slice
}

type OnlyAC = WithOptionalField<void>

fun demo() {
    // field `b` may be omitted
    val v: OnlyAC = { a: 10, c: "" };
}

Optional void parameters

The same convention applies to function parameters: a trailing void parameter may be omitted at the call site. This is convenient for generic helpers where “no argument” is naturally represented by void.
fun format<T1 = void, T2 = void>(msg: string, arg1: T1, arg2: T2) {
    // ...
}

format("hello");
format("value is {}", 42);
format("pair is {} and {}", "str", beginCell());
For example, body of createMessage can be omitted because its type defaults to TBody = void.

void inside unions for serialization

T | void is a valid serializable union. On the wire, void means empty slice: nothing is written on serialization, and an empty input slice deserializes to the void variant. This expresses “T or zero bits” — different from T | null, which always emits at least the Maybe bit.
struct (0x01) Some { x: int32 }

// either Some (0x01 + int32) — or empty slice
type SomeOrEnd = Some | void

// either 32 bits — or empty slice
type Int32OrEnd = int32 | void

// 0+int32, 1+int64, or empty slice
type WideOrEnd = int32 | int64 | void
void does not participate in the prefix tree built for the other variants; it must come last and is matched by an empty slice. After deserialization, the value can be tested with someVar is void, or with match (v) { ... void => ... }. See serialization rules for details.

never

A function that never returns is never. A function whose execution always terminates by throwing an error has return type never:
fun accessDenied(): never {
    throw 403
}

fun validate(userId: int) {
    if (userId > 0) {
        return userId;
    }
    accessDenied();
    // no `return` statement is needed
}
In a match expression, branches that do not return are inferred as never:
val result = match (ownerKey) {
    ADMIN_KEY => true,     // `bool`
    else => throw 403,     // `never`
};
// result is `bool`

Implicit never in unreachable branches

The type never represents values that cannot exist. It occurs implicitly in branches that the type system can prove to be unreachable. Here, the condition cannot be satisfied:
fun demo(v: int | slice) {
    if (v is builder) {
        // v is `never`
    }
}
Another case arises when a non-nullable type is checked against null:
fun demo(v: int) {
    if (v == null) {
        // v is `never`
        v + 10;   // error, can not apply `+` `never` and `int`
    }
    // v is `int` again
}
Encountering never in a compilation error typically indicates an issue in the preceding code.

Stack layout and serialization

Both void and never represent the absence of a value. At the TVM level, neither type produces a value on the stack and occupies zero stack slots and zero bits. For example, a function with return type void does not place any value onto the stack.