> 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).

# Value destruction

As soon as a value/object is no longer used, Mojo destroys it. Mojo does *not*
wait until the end of a code block—or even until the end of an expression—to
destroy an unused value. It destroys values using an "as soon as possible"
(ASAP) destruction policy that runs after every sub-expression. Even within an
expression like `a+b+c+d`, Mojo destroys the intermediate values as soon as
they're no longer needed.

Mojo uses static analysis at compile-time to determine the last use of a value.
At that point, it immediately ends the value's lifetime and calls the *implicit*
`__del__()` destructor. You can override `__del__()` in your structs to perform
any required cleanup.

:::note
For types that require stronger guarantees, Mojo also supports *explicit*
compiler-checked destructors, which provide an additional layer of safety for
managing cleanup. For details, read about [explicit
destruction](#explicitly-destroyed-types) below.
:::

For example, notice when the `__del__()` destructor is called for each instance
of `Balloon`:

```mojo
@fieldwise_init
struct Balloon(Writable):
    var color: String

    def write_to(self, mut writer: Some[Writer]):
        writer.write(String("a ", self.color, " balloon"))

    def __del__(deinit self):
        print("Destroyed", String(self))

def main():
    var a = Balloon("red")
    var b = Balloon("blue")
    print(a)
    # a.__del__() runs here for "red" Balloon

    a = Balloon("green")
    # a.__del__() runs immediately because "green" Balloon is never used

    print(b)
    # b.__del__() runs here
```

```output
a red balloon
Destroyed a red balloon
Destroyed a green balloon
a blue balloon
Destroyed a blue balloon
```

Notice that each initialization of a value is matched with a call to the
destructor, and `a` is actually destroyed multiple times—once for each time it
receives a new value.

Also notice that this `__del__()` implementation doesn't actually do
anything. Most structs don't require a custom destructor, and Mojo automatically
adds a no-op destructor if you don't define one.

The `__del__()` method takes its argument using the `deinit`
[argument passing
convention](/docs/manual/values/ownership/#argument-passing-conventions),
which indicates that the value is being deinitialized.

## Default destruction behavior

You may be wondering how Mojo can destroy a type without a custom destructor, or
why a no-op destructor is useful. If a type is simply a collection of fields,
like the `Balloon` example, Mojo only needs to destroy the fields: `Balloon`
doesn't dynamically allocate memory or use any long-lived resources (like file
handles). There's no special action to take when a `Balloon` value is destroyed.

When a `Balloon` value is destroyed, the `String` value in its `color` field is
no longer used, and it is also immediately destroyed.

The `String` value is a little more complicated. Mojo strings are mutable. The
`String` object has an internal buffer—a
[`List`](/docs/std/collections/list/List/) field,
which holds the characters that make up the string. A `List` stores
its contents in dynamically allocated memory on the heap, so the string can
grow or shrink. The string itself doesn't have any special destructor logic,
but when Mojo destroys a string, it calls the destructor for the
`List` field, which de-allocates the memory.

Since `String` doesn't require any custom destructor logic, it has a no-op
destructor: literally, a `__del__()` method that doesn't do anything.
This may seem pointless, but it means that Mojo can call the destructor on any
value when its lifetime ends. This makes it easier to write generic containers
and algorithms.

### Benefits of ASAP destruction

Similar to other languages, Mojo follows the principle that objects/values
acquire resources in a constructor (`__init__()`) and release resources in a
destructor (`__del__()`). However, Mojo's ASAP destruction has some advantages
over scope-based destruction (such as the C++ [RAII
pattern](https://en.cppreference.com/w/cpp/language/raii), which waits until
the end of the code scope to destroy values):

- Destroying values immediately at last-use composes nicely with the "move"
  optimization, which transforms a "copy+del" pair into a "move" operation.

- Destroying values at end-of-scope in C++ is problematic for some common
  patterns like tail recursion, because the destructor call happens after the
  tail call. This can be a significant performance and memory problem for
  certain functional programming patterns, which is not a problem in Mojo,
  because the destructor call always happens before the tail call.

The Mojo destruction policy is more similar to how Rust and Swift work, because
they both have strong value ownership tracking and provide memory safety. One
difference is that Rust and Swift require the use of a [dynamic "drop
flag"](https://doc.rust-lang.org/nomicon/drop-flags.html)—they maintain hidden
shadow variables to keep track of the state of your values to provide safety.
These are often optimized away, but the Mojo approach eliminates this overhead
entirely, making the generated code faster and avoiding ambiguity.

## Destructors {#destructor}

Mojo calls a value's destructor (`__del__()` method) when the value's lifetime
ends (typically the point at which the value is last used). As we mentioned
earlier, Mojo provides a default, no-op destructor for all types, so in most
cases you don't need to define the `__del__()` method.

You should define the `__del__()` method to perform any kind of cleanup the
type requires. Usually, that includes freeing memory for any fields where you
dynamically allocated memory (for example, via `UnsafePointer`) and
closing any long-lived resources such as file handles.

However, any struct that is just a simple collection of other types does not
need to implement the destructor.

For example, consider this simple struct:

```mojo
@fieldwise_init
struct Balloons:
    var color: String
    var count: Int
```

There's no need to define the `__del__()` destructor for this, because it's a
simple collection of other types (`String` and `Int`), and it doesn't
dynamically allocate memory.

Whereas, the following struct must define the `__del__()` method to free the
memory allocated by its
[`UnsafePointer`](/docs/std/memory/unsafe_pointer/UnsafePointer/):

```mojo
struct HeapArray(Writable):
    var data: UnsafePointer[Int, MutExternalOrigin]
    var size: Int

    def __init__(out self, *values: Int):
        self.size = len(values)
        self.data = alloc[Int](https://mojolang.org/docs/manual/lifecycle/self.size)
        for i in range(self.size):
            (self.data + i).init_pointee_copy(values[i])

    def write_to(self, mut writer: Some[Writer]):
        writer.write("[")
        for i in range(self.size):
            writer.write(self.data[i])
            if i < self.size - 1:
                writer.write(", ")
        writer.write("]")

    def __del__(deinit self):
        print("Destroying", self.size, "elements")
        for i in range(self.size):
            (self.data + i).destroy_pointee()
        self.data.free()

def main():
    var a = HeapArray(10, 1, 3, 9)
    print(a)
```

```output
[10, 1, 3, 9]
Destroying 4 elements
```

The destructor takes its `self` argument using the `deinit` [argument
convention](/docs/manual/values/ownership#argument-conventions), which
grants exclusive ownership of the value and marks it as destroyed at the
end of the function. (For more information on the `deinit` convention, see
[Instance initialization](/docs/manual/lifecycle/initialization/)).

Note that a pointer doesn't *own* any values in the memory it points to, so
when a pointer is destroyed, Mojo doesn't call the destructors on those values.

So in the `HeapArray` example above, calling `free()` on the pointer releases
the memory, but doesn't call the destructors on the stored values. To invoke
the destructors, use the `destroy_pointee()` method provided by the
`UnsafePointer` type.

:::note

You shouldn't call the destructor explicitly. If you need to ensure that a
destructor is called at a specific point, use the discard pattern described in
[explicit lifetime extension](#explicit-lifetime-extension).

:::

It's important to notice that the `__del__()` method is an "extra" cleanup
event, and your implementation does not override any default destruction
behaviors. For example, Mojo still destroys all the fields in `Balloons` even
if you add a `__del__()` method that does nothing:

```mojo
@fieldwise_init
struct Balloons:
    var color: String
    var count: Int

    def __del__(deinit self):
        # Mojo destroys all the fields when they're last used
        pass
```

However, the `self` value inside the `__del__()` destructor is still whole (so
all fields are still usable) until the destructor returns, as we'll discuss
more in the following section.

## Explicitly-destroyed types

Mojo supports both implicit and explicit value destruction. Each approach
balances different needs around safety, performance, and overhead.

One way to think about explicitly destroyed types is that they create a
future requirement, a promise of action. Creating an instance commits you
to performing specific actions (for example, flushing, closing, committing,
or discarding) when the value has no further uses.

This ability to "control the future" is a powerful tool for developers.
It allows teardown requirements to be added directly into the type
system. It guides users toward valid cleanup paths, making programs that
skip those paths impossible to compile and elevating safe destruction
to a first-class part of a value's lifecycle.

With *implicit destruction*, the compiler manages cleanup. It automatically
calls `__del__()` when a value has no further uses, based on lifetime analysis.
You do not invoke the destructor yourself.

With *explicit destruction*, you take over cleanup responsibilities. You must
define and call a named cleanup method (such as `cleanup()` or
`save_and_close()`) and you must call that cleanup method before the end of the
scope in which your value lives. The compiler disables automatic destruction. It
requires you to consume the value. Failing to do so results in a compiler error.

### Using `@explicit_destroy`

Most types use implicit destruction. When a value's lifetime ends, Mojo
automatically calls `__del__()` to clean up. This works for most cases: memory
is freed and simple resources are released automatically.

Some types need more control. A buffered file writer must flush before closing.
A transaction must commit or roll back. A lock must be released explicitly with
proper error handling. These operations may fail, require a specific order, or
depend on deliberate decisions about how cleanup occurs.

The `@explicit_destroy` decorator disables automatic destruction. Instead, you
define named destructor methods that must be called intentionally. The compiler
enforces this requirement: failing to destroy the value produces a
compile-time error.

### Building explicit destruction

To build explicit destruction, define a method that takes a `deinit self`
argument and decorate the type with `@explicit_destroy`. This enables
compile-time checks for your deinitializers and gives you space to add actions
that must be performed before destruction.

Unlike the `__del__()` method, named explicit destructors can raise errors.
This gives you control over how an instance is deallocated. If a destructor
raises an error, the call stack can return to logic that selects an alternative
cleanup path, such as invoking a secondary destructor.

```mojo
@explicit_destroy("Must call save_and_close() or discard()")
struct FileBuffer:
    var path: String
    var data: String

    def __init__(out self, path: String):
        self.path = path
        self.data = ""

    def write(mut self, content: String):
        self.data += content

    # Choose one cleanup path from these two options
    def save_and_close(deinit self) raises:
        write_to_disk(self.path, self.data)

    def discard(deinit self):
        # Abandon buffered data without writing
        pass
```

When used, you must invoke one of the destructors:

```mojo
def write_log(path: String, message: String) raises:
    var buffer = FileBuffer(path)
    buffer.write(message)
    buffer^.save_and_close()  # Required: explicit destruction
```

The compiler verifies that a destructor is called for each explicitly destroyed
value and emits an error if it cannot find one. If you supply a custom message
to `@explicit_destroy`, that message appears in the error output.

```mojo
def broken_write(path: String, message: String):
    var buffer = FileBuffer(path)
    buffer.write(message)
    # ERROR: error: 'buffer' abandoned without being explicitly destroyed:
    #                Must call save_and_close() or discard()
```

### When to use explicit destruction

Use `@explicit_destroy` when cleanup cannot be handled automatically and must be
controlled in code. Common cases include:

- Cleanup can fail and requires error handling.
- Multiple cleanup paths are possible.
- The order of cleanup operations matters.
- Cleanup is expensive and should be deliberate.

Examples:

```mojo
# Multiple cleanup paths
@explicit_destroy("Use commit() or rollback()")
struct Transaction:
    def commit(deinit self) raises: # Requires error handling
        ...
    def rollback(deinit self):
        ...

# Order matters
@explicit_destroy("Use unlock()")
struct MutexGuard:
    # Must be called to release the lock before other operations
    def unlock(deinit self):
    ...

# Cleanup is expensive
@explicit_destroy("Use finalize() to commit expensive computation")
struct BatchProcessor:
    # Expensive operation user should explicitly trigger
    def finalize(deinit self) raises:
        ...
```

### Explicit destruction in generic code

Generic code parameterized over `AnyType` can accept both implicitly and
explicitly destructible values. However, it cannot automatically destroy
values that require explicit destruction.

Because explicit destructors are type-specific, generic code has no way
to name or invoke them unless the type is constrained:

```mojo
def generic_function_1[T: AnyType](https://mojolang.org/docs/manual/lifecycle/var value: T.md):
    # ERROR: 'value' abandoned without being explicitly destroyed:
    #        Unhandled explicitly destroyed type AnyType
    pass  # value abandoned here

def generic_function_2[T: ImplicitlyDestructible](https://mojolang.org/docs/manual/lifecycle/var value: T.md):
    # OK: T can be implicitly destroyed
    pass  # value.__del__() called automatically
```

If a generic function may consume its argument, it must ensure that the value
can be safely destroyed at the end of its lifetime. Generic code has no way to
invoke type-specific explicit destructors unless that behavior is constrained.
Choose one of the following approaches:

- Accept the value by mutable or immutable reference instead of by ownership,
  so the function does not consume it.
- Return the value or transfer ownership instead of consuming it.
- Require implicit destruction with the ImplicitlyDestructible trait, which
  guarantees that a `__del__()` destructor is available and can be called
  automatically:

  ```mojo
  def generic_function_3[T: ImplicitlyDestructible](https://mojolang.org/docs/manual/lifecycle/var value: T.md):
      pass  # value.__del__() called automatically
  ```

## The `deinit` argument convention

In a method, adding the `deinit` convention to `self` means
the method fully consumes the value by the time it returns.
Specifically, `deinit self` signals that:

- The method takes ownership of `self`.
- No automatic destructor is called after the method returns.
- Fields may be transferred or explicitly destroyed inside the method.
- Additional cleanup work may take place within the method.
- The value is considered destroyed when the method completes.

```mojo
@explicit_destroy
struct Example:
    var field: String

    def consume_example(deinit self, mut other: Self):
        # Can transfer field values
        other.field = self.field^
        # No automatic destructor called
        # self is considered destroyed when method returns
```

Methods marked with `deinit self` can transfer ownership of `self` to
other `deinit` methods. Chaining deinitialization methods allows cleanup
responsibilities to be delegated:

```mojo
def cleanup_method1(deinit self):
    self^.cleanup_method2()

def cleanup_method2(deinit self):
    # Actually perform the cleanup
    pass
```

## Field lifetimes

In addition to tracking the lifetime of all objects in a program, Mojo also
tracks each field of a structure independently. That is, Mojo keeps track of
whether a "whole object" is fully or partially initialized/destroyed, and it
destroys each field independently with its ASAP destruction policy.

For example, consider this code that changes the value of a field:

```mojo
@fieldwise_init
struct Balloons:
    var color: String
    var count: Int

def main():
    var balloons = Balloons("red", 5)
    print(balloons.color)
    # balloons.color.__del__() runs here, because this instance is
    # no longer used; it's replaced below

    balloons.color = "blue"  # Overwrite balloons.color
    print(balloons.color)
    # balloons.__del__() runs here
```

The `balloons.color` field is destroyed after the first `print()`, because Mojo
knows that it will be overwritten below. You can also see this behavior when
using the transfer sigil:

```mojo
def consume(var arg: String):
    pass

def use(arg: Balloons):
    print(arg.count, arg.color, "balloons.")

def consume_and_use():
    var balloons = Balloons("blue", 8)
    consume(balloons.color^)
    # String(take=) runs here, which invalidates balloons.color
    # Now balloons is only partially initialized

    # use(balloons)  # This fails because balloons.color is uninitialized

    balloons.color = String("orange")  # All together now
    use(balloons)  # This is ok
    # balloons.__del__() runs here (and only if the object is whole)
```

Notice that the code transfers ownership of the `color` field to `consume()`.
For a period of time after that, the `color` field is uninitialized. Then
`color` is reinitialized before it is passed to the `use()` function. If you try
calling `use()` before `color` is re-initialized, Mojo rejects the code with an
uninitialized field error.

Also, if you don't re-initialize `color` by the end of the `balloons` lifetime,
the compiler complains because it's unable to destroy a partially initialized
object.

Mojo's policy here is powerful and intentionally straight-forward: fields can
be temporarily transferred, but the "whole object" must be constructed with the
aggregate type's initializer and destroyed with the aggregate destructor. This
means it's impossible to create an object by initializing only its fields, and
it's likewise impossible to destroy an object by destroying only its fields.

### Field lifetimes during move and destruction {#field-lifetimes-during-destruct-and-move}

Both the consuming move constructor and the destructor take their operand
with the `deinit` argument convention. This grants exclusive ownership of
the value and marks it as destroyed at the end of the function. Within the
function body, Mojo's ASAP policy still applies to fields: each field is
destroyed immediately after its last use.

To read more about the `deinit` convention, see
[Instance initialization](/docs/manual/lifecycle/initialization/).

## Explicit lifetime extension

Most of the time, Mojo's ASAP destruction "just works." Very rarely, you may
need to explicitly mark the last use of a value to control when its destructor
runs. Think of this as an explicit last-use marker for the lifetime checker, not
a general-purpose pattern.

You might do this:

- When writing tests that intentionally create otherwise-unused values (to avoid
  warnings or dead-code elimination).

- When writing unsafe/advanced code (for example, code that manipulates a
  value's [origin](/docs/manual/values/lifetimes/)).

- When you need deterministic destructor timing relative to specific side
  effects (such as logging or profiling).

Mark the last use by assigning the value to the `_` discard pattern at the point
where it is okay to destroy it. This sets the last use at that line, so the
destructor runs immediately after the statement:

```mojo
# Without explicit extension: s is last used in the print() call, so it is
# destroyed immediately afterwards.
var s = "abc"
print(s)  # s.__del__() runs after this line

# With explicit extension: push last-use to the discard line.
var t = "xyz"
print(t)

# ... some time later
_ = t  # t.__del__() runs after this line
```

:::note

Previous versions of Mojo required the transfer sigil (`^`) when discarding a
move-only type. This is no longer required, since the compiler doesn't actually
move the discarded value. For more information on the transfer sigil, see the
section on
[ownership transfer](/docs/manual/values/ownership/#transfer-arguments-var-and-).

:::
