Optional Chaining
The optional chaining operator ?. provides safe member access on nullable values. If the left-hand side is null, the entire expression short-circuits to null instead of causing a runtime error.
Basic Usage
struct User {
name: str,
}
fn main() {
let user: User? = User { name: "Ada" };
let name: str? = user?.name; // "Ada"
let nobody: User? = null;
let n: str? = nobody?.name; // null
}
The result of a ?. expression is always nullable (T?), even if the accessed field itself is non-nullable.
Chaining Multiple Levels
?. can be chained through multiple levels of nullable fields:
struct Z {
val: i32,
}
struct Y {
z: Z?,
}
struct X {
y: Y?,
}
fn main() {
let x: X? = X { y: Y { z: Z { val: 42 } } };
let v: i32? = x?.y?.z?.val; // 42
let x_null: X? = null;
let v2: i32? = x_null?.y?.z?.val; // null (short-circuits at x_null)
}
Each ?. step checks for null before proceeding. If any step in the chain is null, the rest is skipped and the entire expression evaluates to null.
Result Type
The ?. operator always produces a nullable type. Assigning the result to a non-nullable variable is a compile error:
struct User {
name: str,
}
fn main() {
let user = User { name: "Ada" };
let name: str = user?.name; // error: type mismatch in assignment
}
Use a nullable type for the result:
let name: str? = user?.name; // OK
Mixing ?. and .
When chaining through a nullable value into non-nullable fields, each step after the first ?. must also use ?.:
struct B {
c: i32,
}
struct A {
b: B, // non-nullable
}
fn main() {
let a: A? = A { b: B { c: 1 } };
// Correct:
let v: i32? = a?.b?.c;
// Error — cannot use `.` after `?.`:
// let v: i32? = a?.b.c;
}
Once a chain enters nullable context via ?., all subsequent member accesses must use ?. to propagate the nullable state.
Null Checking
After optional chaining, use standard null comparisons to unwrap the result:
let v: i32? = x?.y?.z?.val;
if v != null {
// v is i32? here, but known non-null
_ = os.write("value is {v}\n");
} else {
_ = os.write("value is null\n");
}
See also
- Type System — nullable types (
T?) - Operators — precedence table (
.and?.share level 17) - Control Flow — null checks and conditionals