@explicit_destroy
The @explicit_destroy decorator disables automatic destruction via the
__del__() method and requires explicit cleanup through named destructor
methods. When applying this decorator, you must call a named destructor
method to consume the value. If you don't, the compiler emits an error.
Use explicit destruction when cleanup must be performed deliberately, may fail and require error handling, or your type offers multiple valid ways to end a value’s lifetime (such as saving to file and closing the file descriptor, or quitting without saving).
Implicit vs. explicit destruction
Mojo supports two destruction models. Most types rely on compiler-managed lifetime analysis, while some types opt into explicit control for stronger cleanup guarantees.
Implicit destruction (the default): The compiler automatically calls
__del__() when a value has no further uses. Cleanup is triggered by Mojo’s
lifetime analysis and requires no manual intervention. You may override
__del__() in your type, but the compiler does not verify that cleanup is
performed intentionally.
Explicit destruction: You intentionally call named destructor methods
(such as cleanup() or save_and_close()). The compiler disables automatic
destruction and enforces explicit consumption. Failing to call a destructor
before a value leaves scope results in a compile-time error.
Most types use implicit destruction. Explicit destruction adds a layer of safety when cleanup may fail, requires error handling, or benefits from deliberate control over how a value’s lifetime ends.
Basic usage
To opt into explicit, compiler-enforced destruction, mark types with
@explicit_destroy and provide named destructor methods that use the
deinit self argument convention:
@explicit_destroy
struct FileBuffer:
var path: String
var data: String
fn __init__(out self, path: String):
self.path = path
self.data = ""
fn write(mut self, content: String):
self.data += content
fn save_and_close(deinit self) raises:
write_to_disk(self.path, self.data)Declaring @explicit_destroy requires you to add intentional calls to
type-specific destructors before the end of scope:
fn write_log(path: String, message: String) raises:
var buffer = FileBuffer(path)
buffer.write(message)
buffer^.save_and_close() # Required before `buffer` leaves scopeIf you omit this call, the compiler emits an error.
Custom error messages
To improve compiler diagnostics for explicit destruction, include a custom error message with the decorator:
@explicit_destroy("Must call save_and_close() or discard()")
struct FileBuffer:
fn save_and_close(deinit self) raises:
write_to_disk(self.path, self.data) # Store data
fn discard(deinit self):
pass # Abandon without writingRelated
- Death of a value - Complete coverage of value destruction and lifetime management
AnyType- Base trait for all typesImplicitlyDestructible- Trait for automatically destructible types
Was this page helpful?
Thank you! We'll create more content like this.
Thank you for helping us improve!