Documentation Index
Fetch the complete documentation index at: https://companyname-a7d5b98e-security-edits.mintlify.app/llms.txt
Use this file to discover all available pages before exploring further.
Tolk supports nullable types T?: they can hold a null value and are a shorthand for T | null. Any type can be made nullable: primitive types, structures, and other composites.
The special null value cannot be assigned to a non-nullable type.
Null safety
The compiler enforces null safety: nullable values cannot be accessed without an explicit check.
var value = x > 0 ? 1 : null; // int?
value + 5; // error
// A check is required
if (value != null) { // value is `int`
value + 5; // ok
b.storeInt(value); // ok
}
When a variable has no explicit type, its type is inferred from the initial assignment. Nullable variables must be declared explicitly:
// Tolk can infer `int`, not `int?`
var i = 0;
i = null; // error, can't assign `null` to `int`
i = maybeInt; // error, can't assign `int?` to `int`
// Type ascription is mandatory for nullables
var i: int? = 0;
i = null; // ok
When the initial value is null, the type must be specified:
var i: int? = null;
// or
var i = null as int?;
Smart casts
The nullable type is narrowed after the null check. This feature, known as smart casts, is available in many general-purpose languages.
// Example 1
if (lastCell != null) {
// here, lastCell is `cell`, not `cell?`
}
// Example 2
if (lastCell == null || prevCell == null) {
// both are `null`
return;
}
// here, both lastCell and prevCell are `cell`
// Example 3
var x: int? = ...; // `null` or some int value
if (x == null) {
x = random();
}
// here, x is `int`
// Example 4
while (lastCell != null) {
lastCell = lastCell.beginParse().loadMaybeRef();
}
// here, lastCell is `null`
Smart casts apply to local variables, structure fields, and tensor or tuple indices.
struct HasOpt {
optionalId: int?
}
fun demo(obj: HasOpt) {
if (obj.optionalId != null) {
// obj.optionalId is `int` here
}
}
Smart casts also apply to initial values. Even if a variable is declared as int? but initialized with a number, it remains a safe non-null integer until it is reassigned:
var idx: int? = -1;
// idx is `int`
Null-coalescing operator ??
The ?? operator returns the left operand if it is not null; otherwise it evaluates and returns the right operand. It is similar to ?? in TypeScript:
fun sum(a: int?, b: int?) {
return (a ?? 0) + (b ?? 0)
}
The operator has right associativity, which allows chaining:
val key = env("SECRET_KEY") ?? env("API_KEY") ?? "default-key";
Non-null assertion operator !
The ! operator bypasses the compiler’s nullability check. It is similar to ! in TypeScript and !! in Kotlin.
fun doSmth(c: cell) {}
fun analyzeStorage(nCells: int, lastCell: cell?) {
if (nCells > 0) { // then lastCell is 100% not null
doSmth(lastCell!); // use ! for this fact
}
}
In some cases, the developer has knowledge that the compiler lacks:
// this key exists according to config,
// so one can force `cell` instead of `cell?`
val mainValidators = blockchain.configParam(16)!;
Global variables
Unlike local variables, global variables cannot be smart-cast. The ! operator is the only way to narrow their type:
global gLastCell: cell?
fun demo() {
// this global is presumed to be set elsewhere,
// lets force `cell` instead of `cell?`
doSmth(gLastCell!);
}
The ! operator is useful when conditions outside the code itself guarantee non-nullability.
Stack layout and serialization
Primitives like int or cell, when nullable, are serialized as a TVM value or null.
Nullable structures and other composites are represented as tagged unions:
- if their type is
null, they are serialized as 0;
- otherwise, they are serialized as
1 followed by the non-null value.
The address? type is an exception, and it is serialized in a different way.