4.3 Initialization state
Mojo tracks two kinds of initialization for structs: fieldwise and logical.
Fieldwise initialization means every field contains a valid value. Logical initialization means the instance as a whole is valid and ready to use. A struct needs both before you can use it.
The basics
You create struct instances by calling the __init__() initializer:
struct Person:
var name: String
var age: Int
def __init__(out self, name: String, age: Int):
self.name = name
self.age = age
def main():
var me = Person("Alice", 30)
Calling Person("Alice", 30) is syntactic sugar for calling the initializer directly:
var me: Person
me = Person.__init__("Alice", 30) # Identical
When constructing a Person, the compiler allocates the necessary storage and __init__() initializes that memory.
Fieldwise vs logical initialization
Initializing a struct by assigning values directly to its fields may populate the data, but it doesn't make the instance usable:
@fieldwise_init
struct Person(Writable):
var name: String
var age: Int
def main():
var me: Person
me.name = "Alice"
me.age = 25
print(me) # Error
# error: 'me' used with all fields manually initialized
# but without calling an '__init__' method
Show error output ✓ Verified · Mojo 1.1.0 (8189361e)
ch04-002.mojo:10:10: error: 'me' used with all fields manually initialized but without calling an '__init__' method
print(me) # Error
^
ch04-002.mojo:7:9: note: 'me' declared here
var me: Person
^
mojo: error: failed to run the pass managerIn this example, all fields contain valid values, but the instance is still not considered initialized.
Assigning every field satisfies fieldwise initialization, but without running an __init__() method, it doesn't satisfy logical initialization.
Construct the value with an initializer to establish both:
var me: Person # Not initialized
me = Person("Alice", 30) # Logically and fieldwise initialized after call
print(me)
After __init__() completes, the instance is safe to use.
Inside __init__()
Within __init__(), self is logically initialized, but its fields are uninitialized. This reverses the situation before calling __init__(), where the fields are initialized but self is not:
def __init__(out self, name: String, age: Int):
# At this point:
# - Logically initialized (self is valid as an instance)
# - Fieldwise uninitialized (fields have no values yet)
self.name = name
self.age = age
# Now both logically and fieldwise initialized
Entering __init__() establishes the instance. It's your responsibility to populate every field:
def __init__(out self, name: String, age: Int):
self.name = name
# Error: field 'age' not initialized in __init__
The __init__() signature doesn't have to mirror the struct's fields. You can use parameters, constants, or external values to initialize them:
# Parameters can be used to initialize fields
self._store = List[T](capacity=Count)
# Constants can be used to initialize fields
self.string = ""
# External values can be used to initialize fields
from std.math import pi
self.default_angle = pi / 2.0
self.uuid = MyUUIDImplementation.uuid()
Calling methods
You can't call methods until all fields are initialized:
def __init__(out self, name: String):
self.greet() # Error: self not fully initialized
self.name = name
self.greet() # OK: all fields initialized
Field initialization is limited to __init__() methods.
Regular methods can't initialize individual fields of an out argument, but __init__() methods can.
Adapted from the Mojo Manual and the MAX GPU programming guides, part of the Modular Platform repository (github.com/modular/modular), Copyright (c) Modular Inc., licensed under the Apache License v2.0 with LLVM Exceptions. Changes were made: reformatted as an accessible XYZ web edition with build-verified example outputs. Mojo and MAX are trademarks of Modular Inc.; this edition is not affiliated with or endorsed by Modular. License: Apache-2.0 WITH LLVM-exception.