Optional Types
An Optional<T> value either holds a value of type T or holds no value.
The absence of a value is represented by the none literal.
Declaration Syntax
Optional<T> varName; // default-initialized to none
Optional<T> varName = expr; // initialized with a value of type T
Optional<T> varName = none; // explicitly initialized to none
Parameters
Tis the value type.Tmay be any Slang type including interface types and generics.
Members
| Member | Type | Description |
|---|---|---|
hasValue |
bool |
true when the optional holds a value; false when it is none. |
value |
T |
The held value. Accessing value when hasValue is false is undefined behavior. |
The none Literal
The built-in keyword none represents the absent value.
none is implicitly convertible to any Optional<T>.
Optional<int> a = none; // no value
bool absent = !a.hasValue; // true
Implicit Coercions
Three implicit coercions are defined for Optional<T>:
Value to Optional
Any value of type T is implicitly convertible to Optional<T>.
The resulting optional holds the value and hasValue is true.
int x = 42;
Optional<int> opt = x; // opt.hasValue == true, opt.value == 42
This applies in function call arguments, return statements, and initializers.
none to Optional
none is implicitly convertible to any Optional<T> with hasValue == false.
Optional<float> f = none; // f.hasValue == false
Optional to Optional coercion
Optional<T> is implicitly convertible to Optional<U> when T is implicitly
convertible to U. The coercion preserves the hasValue state: if the source is
none, the result is none; if the source holds a value, the inner value is
converted from T to U and the result holds that converted value.
Optional<int> src = 7;
Optional<float> dst = src; // dst.hasValue == true, dst.value == 7.0
Optional<int> empty = none;
Optional<float> emptyF = empty; // emptyF.hasValue == false
This applies to all coercible inner types, including numeric promotions and concrete-to-interface upcasts:
interface IShape { int area(); }
struct Square : IShape { int side; int area() { return side * side; } }
Optional<Square> sq = Square{ 3 };
Optional<IShape> sh = sq; // sh.hasValue == true, sh.value.area() == 9
Comparison with none
An Optional<T> value may be compared to none using == and !=:
Optional<int> opt = 5;
if (opt != none)
printf("%d\n", opt.value); // prints 5
This is equivalent to testing opt.hasValue.
if (let ...) Syntax
The if (let name = expr) syntax unwraps an Optional<T> in a single step.
The body executes only when expr has a value; inside the body, name is
bound to the unwrapped value of type T.
Optional<int> getVal() { ... }
void example()
{
if (let x = getVal())
{
// x has type int here
printf("%d\n", x);
}
}
Memory Layout
Optional<T> is lowered to a struct with two fields:
struct Optional<T> { T value; bool hasValue; }
The layout follows the same rules as a struct with those two members. The
value field is always present in memory regardless of hasValue; reading
value when hasValue is false is undefined behavior.
📝 Remark: The layout is target-specific and subject to standard struct alignment rules. Do not rely on a specific byte layout for serialization.
Restrictions
Optional<T>cannot be used as the element type of a resource (e.g.,StructuredBuffer<Optional<T>>is not supported).- Accessing
.valuewhen.hasValueisfalseis undefined behavior.