> For the complete Mojo documentation index, see [llms.txt](/llms.txt).
> Markdown versions of all pages are available by appending .md to any URL (e.g. /docs/manual/basics.md).

# Deep dive - Instance initialization

Mojo tracks two kinds of initialization for structs: _fieldwise_ and
_logical_.

Fieldwise initialization means the struct's storage holds valid values
for each field. Logical initialization means the instance as a whole is
in a valid, usable state.

Understanding the difference between these two forms of initialization
is essential for working with structs correctly.

## The basics

You create struct instances by calling the `__init__()` constructor:

```mojo
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
constructor directly:

```mojo
var me: Person
me = Person.__init__("Alice", 30) # Identical
```

When constructing a Person, the compiler first allocates stack
space for the instance `me`, then `__init__()` initializes
that space.

## Fieldwise vs logical initialization

Initializing a struct by assigning values directly to its fields may
populate the data, but it does not make the instance usable:

```mojo
struct Person(Writable):
    var age: Int
    var height: Int

    ...

def main():
    var me: Person
    me.age = 25
    me.height = 6
    print(me)  # Error
    # error: 'me' used with all fields manually initialized
    # but without calling an '__init__' method
```

In this example, all fields contain valid values, but the instance is
still not considered initialized.

Mojo tracks two distinct initialization states:

- Fieldwise initialization means each field contains valid data and the
  instance's storage has been populated.
- Logical initialization means the instance as a whole is valid and its
  `__init__()` method has run.

Fieldwise initialization alone isn't enough. The compiler requires
logical initialization before an instance can be used:

```mojo
var me = Person(25, 6)  # Now OK: __init__() ran
print(me)
```

Calling `__init__()` makes an instance _logically_ initialized. This
process isn't just about assigning data. It ensures that any required
initialization logic ran and that the instance is safe to use.

## Inside `__init__()`

Within `__init__()`, the incoming `self` instance is considered
logically initialized, but its fields are still uninitialized:

```mojo
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
```

This distinction is intentional. Entering `__init__()` establishes the
instance itself, but it's your responsibility to populate its fields.

You must initialize _every field_ in `__init__()`:

```mojo
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
those fields:

```mojo
# Parameters can be used to initialize fields
self._store = List[T](https://mojolang.org/docs/manual/lifecycle/capacity=Count.md)

# 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()
```

You can't call methods until all fields are initialized:

```mojo
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 restricted to `__init__()` methods. Regular
functions can't _initialize_ individual fields of an `out`
argument, but `__init__()` methods can.

## Moving from fields

Moving a value out of a field _deinitializes that field_:

```mojo
struct Person(Writable):
    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("Connor", 25)
    var name_owner = me.name^  # Move value from me.name.
    print(me)     # Error: use of uninitialized value 'me'
```

After the move, the instance is still _logically initialized_, but it's
only partially _fieldwise initialized_. Mojo doesn't allow using
instances with uninitialized fields.

To make the instance usable again, you must reinitialize the moved-from
field:

```mojo
var me = Person("Connor", 25)
var name_owner = me.name^
me.name = "John"      # Reinitialize field
print(me)             # OK
use_value(name_owner) # Now, use name_owner
```

Any moved field requires a new value before you can use the instance
again. Even if _all_ fields are moved, the instance itself remains
logically initialized.

## Mojo's default destructor

`__del__()` is Mojo's default _destructor_ (or _deinitializer_). While it
runs, the instance remains logically initialized, and it becomes
logically deinitialized when the method completes:

```mojo
@fieldwise_init
struct Contact:
    var name: String
    var email: String

    def __del__(deinit self):
        # `self` is initialized
        print("destroying contact")
        # `self is deinitialized

def main():
    var me = Contact("Alice", "alice@example.com")
    # Last use of `me` (and end of scope)
    # Prints "destroying contact" along with a warning
    # since the instance was never used after assignment.
```

## Destructors and moving fields

Destructors are the only place where you can safely move values out of an
instance's fields without having to reinitialize the field before its
next use.

This special rule isn't about where the code lives. It's about
guarantees:

```mojo
def __del__(deinit self):
    var name = self.name^  # OK: can take ownership of fields
```

The compiler knows that destructors like `__del__()` are the final use of
an instance. Because of this, it allows you to move values out of fields
without reinitializing them.

This ensures that fields are only moved out of struct instances when the
compiler can guarantee the instance is at the end of its lifetime.

Like instances, struct fields use ASAP destruction within deinit
methods. For example:

```mojo
struct S:
   var a: String
   var b: String

   def __del__(deinit self):
       # Mojo calls a.__del__() here.
       use(b)
       # Mojo calls b.__del__() here.
```

## The `deinit` convention

A destructor's secret sauce is the `deinit` convention. Because of this,
`deinit self` works in named destructors as well as `__del__()`:

```mojo
struct Parent:
    def consume(deinit self):
        # `consume` is a named destructor
        # Can move values out of fields here too
```

When you use the `deinit self` convention in a method, the compiler
recognizes the method as a destructor and lets you move a field without
reinitializing it:

```mojo
struct Parent:
    def consume(deinit self):
        """valid destructor"""
        var name = self.name^
        print("Name:", name) # Prints name
```

Any struct method that uses `deinit self` as its first argument is a destructor.
As a destructor, you can use this `consume` method for
[explicit destruction types](/docs/manual/lifecycle/death/#explicitly-destroyed-types).

## Explicit and implicit destructors

The `__del__()` method is the core of implicit destruction types. It's
Mojo's default destructor and works hand in hand with
`ImplicitlyDestructible`, a trait that all structs conform to by default.
The compiler calls `__del__()` automatically when it detects the final
use of an implicitly destructible struct value.

As the name suggests, explicit destruction requires you to call a
destructor. An explicitly destroyed type must define at least one
custom destructor, such as the `consume()` method shown in the
previous section. The compiler make sure that the final use of an
explicitly destroyed value will be a call to one of its custom
destructors.

### Using `deinit` with other arguments

The `deinit` convention isn't limited to `self`. You can write methods
and functions that destruct other instances:

```mojo
struct Pair:
    def destroy_other(self, deinit other: Self):
        # Can take from fields of `other` here
```

Like `deinit self`, the `deinit` convention in this example tells the
compiler that `other` is tagged for destruction.

Using `deinit` means `other` is logically deinitialized at the end of the
method. Because of this, it's safe to move values out of `other`, since
the instance's lifetime is guaranteed to complete.

## Normal methods and moving fields

In ordinary methods, you can't move values out of a struct's fields
without reinitializing them:

```mojo
@fieldwise_init
struct MyStruct:
    var name: String

    def move_field(mut self, var new_name: String):
        var name = self.name^ # Error
        # error: 'self.name' is uninitialized at the implicit return
        #        from this function
        print("Name:", name)

def main():
    var instance = MyStruct("Ken")
    instance.move_field("Scott")
    print("Name:", instance.name) # Prints: "Name: Scott"
```

To move safely, reinitialize the instance's fields before the end of
scope. The following update fixes the bug by reinitializing the field:

```mojo
def move_field(mut self, var new_name: String):
    var name = self.name^
    print("Name:", name)  # Prints name
    self.name = new_name^ # reinitialize the name field
```

## Destroying instances

Unless you opt into
[explicit destructors](/docs/manual/lifecycle/death/#explicitly-destroyed-types),
Mojo uses implicit destruction. Both explicit and implicit destructors use ASAP
("As Soon As Possible") destruction:

```mojo
def example():
    var x = SomeType()
    use(x) # last use in scope
    # x destroyed here, immediately after last use

    more_code()  # x is already gone
```

With ASAP, Mojo destroys instances immediately after their last use, not
at the end of scope. This can happen even in the middle of an
expression.

This differs from many languages with compiler-managed lifecycles, where
destruction happens at end of scope. Mojo's ASAP destruction enables
better tail call optimization, but it also means destructors can run
earlier than you might expect.

Mojo also supports _explicitly-destroyed types_, which require you to make an
explicit call to a destructor method. For more information, see
[Explicitly-destroyed types](/docs/manual/lifecycle/death/#explicitly-destroyed-types).

Mojo tracks the lifetimes of values in your code. It knows when you move
out of a value using the transfer operator (`^`), or when you call a
`deinit` method that explicitly destroys it. If the compiler finds a
live value that hasn't otherwise been cleaned up, it uses the implicit
destructor (`__del__(deinit self...)`) to destroy it.

If a type doesn't have an implicit destructor because it requires
explicit destruction, the compiler emits an error, as indicated by the
`@explicit_destroy` decorator.

While the compiler checks that a destructor is called before an
instance goes out of scope, you're responsible for choosing _when_ that
happens. The key differences are:

- Destruction doesn't automatically happen at the point of last use.
- The explicit destructor isn't named `__del__()`.

## Object initialization summary

Creating, using, and destroying instances in Mojo follows a consistent
set of rules based on initialization state.

Create instances by calling `__init__()`. You must initialize every
field in the initializer, especially if you plan to use the instance
within the method scope. You can't call methods on a struct unless all
its fields are initialized.

You must _logically_ initialize an instance before using it, which
means calling the initializer. You must also initialize fields before
accessing them. Partially initialized instances can't be used in many
contexts, including method calls and return values.

For implicitly destroyed structs, the compiler inserts a destructor
call after the instance's last use. For explicitly destroyed values,
you must call a destructor before the instance goes out of scope.
Destruction is compiler-checked, but you choose when it happens.
The value's final use in scope must be that explicit destructor call.

Field operations are also constrained. Fieldwise initialization must
happen inside the `__init__()` constructor. You can move values out of a
field only when the compiler can prove that the instance won't be used
again, or that the field will be reinitialized before the end of a
normal method.

This split between logical and fieldwise initialization gives Mojo
fine-grained control over instance lifetimes, while ensuring that
initialization, use, and destruction remain safe and explicit.
