For the complete Mojo documentation index, see llms.txt. Markdown versions of all pages are available by appending .md to any URL (e.g. /docs/manual/basics.md).
MaybeUninit
struct MaybeUninit[T: AnyType]
Holds memory that may or may not contain an initialized T.
MaybeUninit[T] helps enable low level code deal with uninitialized data.
# The compiler knows `uninit` may contain uninitialized data, so it
# makes no assumptions.
var uninit = MaybeUninit[Int]()
uninit.unsafe_write(42)
# SAFETY: `uninit` now contains an initialized `Int`.
print(uninit.unsafe_assume_init()) # 42
However, improper usage of this type can lead to undefined behavior:
var uninit = MaybeUninit[Int]()
# Undefined Behavior: reading uninitialized memory 👻
print(uninit.unsafe_assume_init())
Examples​
Out-arguments across C-FFI​
A C function that fills an out-argument writes into memory the caller
provides. Without MaybeUninit[T], you'd need a placeholder value to point
at, even though the call is about to overwrite it entirely. Because
MaybeUninit[T] has the same layout as T, you can pass it directly as the
out-pointer and skip constructing a placeholder.
from std.ffi import external_call, c_long, c_int
from std.memory import MaybeUninit
struct CTimeSpec(ImplicitlyCopyable, Writable):
var tv_sec: c_long
var tv_nsec: c_long
def main():
# Create an uninitialized `CTimeSpec`
var spec = MaybeUninit[CTimeSpec]()
# int clock_gettime(clockid_t clockid, struct timespec *res)
var result = external_call["clock_gettime", c_int](
c_int(1), # CLOCK_MONOTONIC on linux
Pointer(to=spec), # out-pointer: clock_gettime() fills this in
)
if result == 0:
# SAFETY: `clock_gettime` succeeded, so `spec` is now initialized.
print(spec.unsafe_assume_init()) # CTimeSpec(tv_sec=.., tv_nsec=..)
# On failure, `spec` is left uninitialized and unread.
Uninitialized fields​
Wrap a field in MaybeUninit[T] to leave it uninitialized rather than
filling it with a placeholder or default value. Track whether it
actually holds a value separately, as this minimal reimplementation of
Optional does.
from std.memory import MaybeUninit
struct Optional[T: Movable & Deinitable]:
var storage: MaybeUninit[Self.T]
var initialized: Bool
def __init__(out self):
# No value yet, leave `storage` uninitialized.
self.storage = {}
self.initialized = False
def __init__(out self, var value: Self.T):
self.storage = {value^}
self.initialized = True
def __init__(out self, *, deinit move: Self):
self.storage = {}
self.initialized = move.initialized
# If `move` was initialized, write its contents into our storage.
if move.initialized:
self.storage.unsafe_write(move.storage^.unsafe_assume_init())
else:
move.storage^.unsafe_forget()
def __deinit__(deinit self):
# If we're initialized, call the `__deinit__` on the held value,
# otherwise we can just forget the storage.
if self.initialized:
self.storage^.unsafe_deinit()
else:
self.storage^.unsafe_forget()
Deinitialization, moving, and copying​
Unlike T, a MaybeUninit[T] does not automatically perform T's
lifecycle operations. Its lifecycle trait conformances depend on whether
those operations can be performed trivially:
MaybeUninit[T]isMovablewhenTis trivially movable.MaybeUninit[T]isImplicitlyCopyablewhenTis trivially copyable.MaybeUninit[T]isDeinitablewhenTis trivially deinitializable.
This matters most for types with non-trivial implicit deinitializers. To
prevent accidental resource leaks, if T is not trivially deinitializable,
MaybeUninit[T] is an explicitly destroyed type and must be consumed.
String is not trivially deinitializable, so MaybeUninit[String] is not
Deinitable.
var uninit = MaybeUninit[String]()
# error: `uninit` abandoned without being explicitly destroyed
Use unsafe_deinit() when the storage contains a live T whose
deinitializer must run.
var uninit = MaybeUninit[String]()
uninit.unsafe_write("hello")
# SAFETY: `uninit` contains a live `String`.
uninit^.unsafe_deinit()
Use unsafe_forget() when the storage does not contain a live T:
var uninit = MaybeUninit[String]()
# SAFETY: Nothing was ever written into the storage, so there is no `String`
# to destroy.
uninit^.unsafe_forget()
String also demonstrates why these lifecycle operations are independent.
A String is trivially movable, so MaybeUninit[String] can itself be
moved:
var uninit = MaybeUninit[String]()
uninit.unsafe_write("hello")
var moved = uninit^ # OK: `String` is trivially movable.
However, String is not trivially copyable so MaybeUninit[String] is not
ImplicitlyCopyable. Copying it implicitly is a compile-time error.
var uninit = MaybeUninit[String]()
var copy = uninit # error: `MaybeUninit[String]` is not `ImplicitlyCopyable`
For types such as Int that are trivially movable, copyable, and
deinitializable, all of these conditions are satisfied and
MaybeUninit[Int] behaves like an ordinary trivial value.
var uninit = MaybeUninit[Int]()
var copy = uninit # OK: Int is trivially copyable
var moved = uninit^ # OK: Int is trivially movable
# OK: No need to explicitly consume `MaybeUninit[Int]` since `Int` is
# trivially deinitializable.
Layout​
MaybeUninit[T] has the same size and alignment as T:
size_of[MaybeUninit[T]]() == size_of[T]()align_of[MaybeUninit[T]]() == align_of[T]()
Parameters​
- ​T (
AnyType): The held element type of the wrapper.
Implemented traits​
AnyType,
Copyable (where conforms_to(T, Movable) and T.__move_ctor_is_trivial or conforms_to(T, TrivialRegisterPassable) and conforms_to(T, Copyable) and T.__copy_ctor_is_trivial or conforms_to(T, TrivialRegisterPassable)),
Defaultable,
Deinitable (where conforms_to(T, TrivialRegisterPassable) or T.__del__is_trivial if conforms_to(T, Deinitable) else conforms_to(T, Deinitable)),
ImplicitlyCopyable (where conforms_to(T, TrivialRegisterPassable) if conforms_to(T, TrivialRegisterPassable) else T.__copy_ctor_is_trivial if conforms_to(T, Copyable) else conforms_to(T, Copyable) and conforms_to(T, TrivialRegisterPassable) if conforms_to(T, TrivialRegisterPassable) else T.__move_ctor_is_trivial if conforms_to(T, Movable) else conforms_to(T, Movable)),
Movable (where conforms_to(T, TrivialRegisterPassable) or T.__move_ctor_is_trivial if conforms_to(T, Movable) else conforms_to(T, Movable)),
RegisterPassable (where conforms_to(T, RegisterPassable) and conforms_to(T, TrivialRegisterPassable) if conforms_to(T, TrivialRegisterPassable) else T.__move_ctor_is_trivial if conforms_to(T, Movable) else conforms_to(T, Movable))
Methods​
__init__​
def __init__(out self)
Construct a MaybeUninit in an uninitialized state.
def __init__(out self, var value: T, /) where conforms_to(T, Movable)
Construct a MaybeUninit in an initialized state.
Args:
- ​value (
T): The value to initialize the memory with.
zeroed​
static def zeroed(out result)
Construct a MaybeUninit in an uninitialized state, with the memory set to all 0 bytes.
It depends on T whether zeroed memory makes for proper initialization.
For example, MaybeUninit[Int].zeroed() is initialized,
but MaybeUninit[String].zeroed() is not.
Returns:
Self: A MaybeUninit with the memory set to all 0 bytes.
unsafe_write​
def unsafe_write(mut self, var value: T, /) where conforms_to(T, Movable)
Initialize this memory with the given value.
This overwrites any previous value without destroying it.
This means, if a previous T existed in the memory, that old instance
will not be destroyed, potentially leading to memory leaks.
Safety:
- If the memory is already initialized, calling this leaks the
previous value: its destructor never runs. Call
unsafe_deinit()first if the previous value needs to be destroyed.
Args:
- ​value (
T): The value to store in memory.
unsafe_assume_init​
def unsafe_assume_init(deinit self) -> T where conforms_to(T, Movable)
Takes ownership of the contained value.
Calling this method assumes that the memory is initialized. The
value is moved out of the MaybeUninit and returned to the
caller. After this call, the memory is considered uninitialized.
Safety:
- The memory must be initialized with a live
Tvalue. Calling this on uninitialized memory reads an invalid bit pattern asT, which is undefined behavior.
Returns:
T: The initialized value that was stored in this container.
def unsafe_assume_init(ref self) -> ref[self_is_mut] T
Returns a reference to the internal value.
Calling this method assumes that the memory is initialized.
Safety:
- The memory must be initialized with a live
Tvalue. Calling this on uninitialized memory produces a reference to an invalid bit pattern, which is undefined behavior if the reference is read.
Returns:
ref[self_is_mut] T: A reference to the internal value.
unsafe_deinit​
def unsafe_deinit(deinit self) where conforms_to(T, Deinitable)
Destroys the contained value.
Calling this method assumes that the memory is initialized. It runs
T's destructor on the contained value. After this call, the memory
is considered uninitialized.
Safety:
- The memory must be initialized with a live
Tvalue. Calling this on uninitialized memory runsT's destructor on an invalid bit pattern, which is undefined behavior.
unsafe_forget​
def unsafe_forget(deinit self)
Discards this MaybeUninit without destroying its contents.
Unlike unsafe_deinit(), this does not run T's destructor. Use
this when the memory is uninitialized, or when the contained value
has already been disposed of some other way.
Safety:
- If the memory is initialized with a value that owns a resource
(for example, an allocation), calling this leaks that resource:
its destructor never runs. Call
unsafe_deinit()instead if the value needs to be destroyed.
unsafe_ptr​
def unsafe_ptr(ref self) -> Pointer[T, origin_of(self)]
Get a pointer to the underlying element.
Note that this method does not assumes that the memory is initialized or not. It can always be called.
Safety:
- The returned pointer may point to uninitialized memory. Reading through it before the memory is initialized is undefined behavior.
Returns:
Pointer[T, origin_of(self)]: A pointer to the underlying element.