Here's a brief summary of some of the major changes in this release, with more detailed information in the following sections:
Mojo now supports Python 3.12 interoperability.
The set of automatically imported entities (types, aliases, functions) into users' Mojo programs has been dramatically reduced. This can break existing user code as users will need to explicitly import what they're using for cases previously automatically included before.
print()
now requires that its arguments conform to the Formattable
trait. This enables efficient stream-based writing by default, avoiding unnecessary intermediate String heap allocations.
The new builtin input()
function prints an optional prompt and reads a line from standard input, in the same way as Python.
Mojo now allows implicit definitions of variables within a fn
in the same way that has been allowed in a def
. The var
keyword is still allowed, but is now optional.
Mojo now diagnoses "argument exclusivity" violations due to aliasing references. Mojo requires references (including implicit references due to borrowed
/inout
arguments) to be uniquely referenced (non-aliased) if mutable. This is a warning in the 24.5 release, but will be upgraded to an error in subsequent releases.
Mojo now supports "conditional conformances" where some methods on a struct have additional trait requirements that the struct itself doesn't.
DTypePointer
, LegacyPointer
, and Pointer
have been removed. Use UnsafePointer
instead. Functions that previously took a DTypePointer
now take an equivalent UnsafePointer
. For more information on using pointers, see Unsafe pointers in the Mojo Manual.
There are many new standard library APIs, with new features for strings, collections, and interacting with the filesystem and environment. Changes are listed in the standard library section.
The VS Code extension now supports a vendored MAX SDK for VS Code, which is automatically downloaded by the extension and it's used for all Mojo features, including the Mojo Language Server, the Mojo debugger, the Mojo formatter, and more.
mojo test
now uses the Mojo compiler for running unit tests. This will resolve compilation issues that sometimes appeared, and will also improve overall test execution times.
Mojo now allows implicit definitions of variables within a fn
in the same way that has been allowed in a def
. The var
keyword is still allowed and still denotes the declaration of a new variable with a scope (in both def
and fn
). Relaxing this makes fn
and def
more similar, but they still differ in other important ways.
Mojo now diagnoses "argument exclusivity" violations due to aliasing references. Mojo requires references (including implicit references due to borrowed
/inout
arguments) to be uniquely referenced (non-aliased) if mutable. This is important for code safety, because it allows the compiler (and readers of code) to understand where and when a value is mutated. It is also useful for performance optimization because it allows the compiler to know that accesses through immutable references cannot change behind the scenes. Here is an invalid example:
fn take_two_strings(a: String, inout b: String): # Mojo knows 'a' and 'b' cannot be the same string. b += afn invalid_access(): var my_string = String() # error: passing `my_string` inout is invalid since it is also passed # borrowed. take_two_strings(my_string, my_string)
This is similar to Swift exclusivity checking and the Rust language sometimes known as "aliasing xor mutability". That said, the Mojo implementation details are somewhat different because lifetimes are embedded in types.
This is a warning in the 24.5 release, but will be upgraded to an error in subsequent releases.
note
Argument exclusivity is not enforced for register-passable types. They are passed by copy, so they don't form aliases.
Mojo now supports "conditional conformances" where some methods on a struct have additional trait requirements that the struct itself doesn't. This is expressed through an explicitly declared self
type:
struct GenericThing[Type: AnyType]: # Works with anything # Sugar for 'fn normal_method[Type: AnyType](self: GenericThing[Type]):' fn normal_method(self): ... # Just redeclare the requirements with more specific types: fn needs_move[Type: Movable](self: GenericThing[Type], owned val: Type): var tmp = val^ # Ok to move 'val' since it is Movable ...fn usage_example(): var a = GenericThing[Int]() a.normal_method() # Ok, Int conforms to AnyType a.needs_move(42) # Ok, Int is movable var b = GenericThing[NonMovable]() b.normal_method() # Ok, NonMovable conforms to AnyType # error: argument type 'NonMovable' does not conform to trait 'Movable' b.needs_move(NonMovable())
Conditional conformance works with dunder methods and other things as well.
As a specific form of "conditional conformances", initializers in a struct may indicate specific parameter bindings to use in the type of their self
argument. For example:
@valuestruct MyStruct[size: Int]: fn __init__(inout self: MyStruct[0]): pass fn __init__(inout self: MyStruct[1], a: Int): pass fn __init__(inout self: MyStruct[2], a: Int, b: Int): passdef test(x: Int): a = MyStruct() # Infers size=0 from 'self' type. b = MyStruct(x) # Infers size=1 from 'self' type. c = MyStruct(x, x) # Infers size=2 from 'self' type.
Mojo now supports named result bindings. Named result bindings are useful for directly emplacing function results into the output slot of a function. This feature provides more flexibility and guarantees around emplacing the result of a function compared to "guaranteed" named return value optimization (NRVO). If a @register_passable
result is bound to a name, the result value is made accessible as a mutable reference.
fn efficiently_return_string(b: Bool) -> String as output: if b: output = "emplaced!" mutate(output) return return "regular return"
If we used a temporary for output
instead, we would need to move into the result slot, which wouldn't work if the result type was non-movable.
In a function with a named result, return
may be used with no operand to signal an exit from the function, or it can be used normally to specify the return value of the function. The compiler will error if the result is not initialized on all normal exit paths from the function.
__setitem__()
now works with variadic argument lists such as:
struct YourType: fn __setitem__(inout self, *indices: Int, val: Int): ...
The Mojo compiler now always passes the "new value" being set using the last keyword argument of the __setitem__()
, e.g. turning yourType1, 2] = 3
into yourType.__setitem__(1, 2, val=3)
. This fixes [Issue #248.
Mojo context managers used in regions of code that may raise no longer need to define a "conditional" exit function in the form of fn __exit__(self, e: Error) -> Bool
. This function allows the context manager to conditionally intercept and handle the error and allow the function to continue executing. This is useful for some applications, but in many cases the conditional exit would delegate to the unconditional exit function fn __exit__(self)
.
Concretely, this enables defining with
regions that unconditionally propagate inner errors, allowing code like:
def might_raise() -> Int: ...def foo() -> Int: with ContextMgr(): return might_raise() # no longer complains about missing returndef bar(): var x: Int with ContextMgr(): x = might_raise() print(x) # no longer complains about 'x' being uninitialized
async
functions now support memory-only results (like String
, List
, etc.) and raises
. Accordingly, both Coroutine
and RaisingCoroutine
have been changed to accept AnyType
instead of AnyTrivialRegType
. This means the result types of async
functions do not need to be Movable
.
async fn raise_or_string(c: Bool) raises -> String: if c: raise "whoops!" return "hello world!"
Note that async
functions do not yet support indirect calls, ref
results, and constructors.
The Reference
type (and many iterators) now use infer-only parameters to represent the mutability of their lifetime, simplifying the interface.
The environment variable MOJO_PYTHON
can be pointed to an executable to pin Mojo to a specific version:
export MOJO_PYTHON="/usr/bin/python3.11"
Or a virtual environment to always have access to those Python modules:
export MOJO_PYTHON="~/venv/bin/python"
MOJO_PYTHON_LIBRARY
still exists for environments with a dynamic libpython
but no Python executable.
The pointer aliasing semantics of Mojo have changed. Initially, Mojo adopted a C-like set of semantics around pointer aliasing and derivation. However, the C semantics bring a lot of history and baggage that are not needed in Mojo and which complicate compiler optimizations. The language overall provides a stronger set of invariants around pointer aliasing with lifetimes and exclusive mutable references to values, etc.
It is now forbidden to convert a non-pointer-typed value derived from a Mojo-allocated pointer, such as an integer address, to a pointer-typed value. "Derived" means there is overlap in the bits of the non-pointer-typed value with the original pointer value. Accordingly, the UnsafePointer
constructor that took an address
keyword argument has been removed.
It is still possible to make this conversion in certain cases where it is absolutely necessary, such as interoperating with other languages like Python. In this case, the compiler makes two assumptions: any pointer derived from a non-pointer-typed value does not alias any Mojo-derived pointer and that any external function calls have arbitrary memory effects.
await
on a coroutine now consumes it. This strengthens the invariant that coroutines can be awaited only once.
Fixed a crash in the Mojo Language Server when importing the current file.
Fixed crash when specifying variadic keyword arguments without a type expression in def
functions, e.g.:
def foo(**kwargs): ... # now works
Mojo now prints ref
arguments and results in generated documentation correctly.
#1734 - Calling __copyinit__
on self causes crash.
#3142 - [QoI] Confusing __setitem__
method is failing with a "must be mutable" error.
#248 - [Feature] Enable __setitem__
to take variadic arguments
#3065 - Fix incorrect behavior of SIMD.__int__
on unsigned types
#3045 - Disable implicit SIMD conversion routes through Bool
#3126 - [BUG] List doesn't work at compile time.
#3237 - [BUG] Difference between __getitem__
and [.]
operator.
#3336 - Fix outdated references to let
in REPL documentation.
The VS Code extension no longer caches the information of the selected MAX SDK, which was causing issues upon changes in the SDK.
The Mojo debugger now stops showing spurious warnings when parsing closures.