Skip to main content
Version: 1.0

v0.25.6 (2025-09-22)

✨ Highlights

  • You can now pip install mojo!

    Although we still love the environment reliability of Pixi, installing Mojo with pip or uv further enhances our interoperability with the Python ecosystem, making it easier to extend your Python code with Mojo. For more information, see the Mojo install guide.

  • We've released a new version of the Mojo extension for Visual Studio Code that now works with both the stable and nightly Mojo releases. You can install the Mojo extension from either the Visual Studio Code Marketplace or the Open VSX Registry.

    The new extension replaces the old stable version, so if you have the stable version installed, you can simply update it to receive the new version. If you have the nightly version of the extension installed, you should uninstall it and install the regular (non-nightly) version. See Add the VS Code extension for more information.

  • New Mojo vision doc explains our motivations and design decisions for the Mojo language.

  • New Mojo roadmap provides a high-level roadmap for the language across multiple phases.

  • Mojo now has support for default trait methods, allowing traits to provide reusable behavior without requiring every conforming struct to re-implement it. Default methods are automatically inherited by conforming structs unless explicitly overridden. See Default method implementations in the Mojo Manual for more information.

  • Added support for many consumer GPUs, including initial support for Apple silicon GPUs. See GPU support for details.

  • The way copying is modeled in Mojo has been overhauled. The Copyable trait has been updated to represent a type that can be explicitly copied (using a copy() method), and a new ImplicitlyCopyable "marker" trait can be used to opt-in to making a type implicitly copyable as well. This swaps the default behavior from being implicitly copyable to being only explicitly copyable. Several standard library traits, types, and functions now require now require explicit Copyable instead of ImplicitlyCopyable. See Standard library changes for more information.

  • Uncaught exceptions or segmentation faults in Mojo programs can now generate stack traces. This is currently only for CPU-based code. To generate a fully symbolicated stack trace, set the MOJO_ENABLE_STACK_TRACE_ON_ERROR environment variable, use mojo build with debug info enabled, e.g. -debug-level=line-tables, and then run the resulting binary.

  • Major standard library improvements include:

    • Making the SIMD type conform to Comparable and EqualityComparable, which means that you can use SIMD values as Dict keys, among other things. For details, see SIMD and related types.

    • A new Some[Trait] utility to make it easier to declare an argument that conforms to a trait. For details, see Other standard library changes.

    • Several enhancements to how iterators work in Mojo, including a new Iterable trait. For more information, see Collections and iterators changes.

Language enhancements

  • Mojo now allows the use of keywords in function names (after def and fn) and in attribute references after a .. This notably allows the use of the match() method in regex libraries even though Mojo takes this as a hard keyword. Uses in other locations can still use backticks:

    struct MatchExample:
    fn match(self): # This is ok now.
    pass

    fn test_match(a: MatchExample):
    a.match() # This is ok now.
    a.`match`() # This is still valid.
  • When generating error messages for complex types involving parameter calls, the Mojo compiler now prints functions parameter values correctly, eliminating a large class of T != T errors that happen with GPU layouts.

Language changes

  • Methods on structs may now declare their self argument with a deinit argument convention. This argument convention is used for methods like __del__() and __moveinit__() to indicate that they tear down the corresponding value without needing its destructor to be run again. Beyond these two methods, this convention can be used to declare "named" destructors, which are methods that consume and destroy the value without themselves running the values destructor. For example, the standard VariadicPack type has these methods:

    struct VariadicPack[...]:
    # implicit destructor
    fn __del__(deinit self): ...
    # move constructor
    fn __moveinit__(out self, deinit existing: Self): ...
    # custom explicit destructor that destroys "self" by transferring all of
    # the stored elements.
    fn consume_elements[
    elt_handler: fn (idx: Int, var elt: element_type) capturing
    ](deinit self): ...

    This argument convention is a fairly narrow power-user feature that is important to clarify the destruction model and make linear types fit into the model better. (A linear type is just a type that has no __del__() method, but provides a destructor that the user must call explicitly. Linear types are a proposed feature that hasn't been fully implemented yet.)

    • The __del__() and __moveinit__() methods should now take their self and existing arguments as deinit instead of either owned or var.

    • The __disable_del keyword and statement has been removed, use deinit methods instead.

  • The Mojo compiler now warns about use of the deprecated owned keyword, please move to var or deinit as the warning indicates.

  • The previously deprecated @value decorator has been removed.

  • Accesses to associated aliases and methods within a trait now require qualified references (prepended with Self.), making it consistent with how accesses to member aliases and methods in a struct require self..

  • The Mojo compiler now raises error on implicit materialization of a non-ImplicitlyCopyable object, please either mark the type to be ImplicitlyCopyable or using materialize[value: T]() to explicitly materialize the parameter into a dynamic value.

    This usually happens when you generate a compile-time value using the alias keyword, then assign it to a runtime variable:

    alias lst = [1, 2, 3, 4]  # Create a compile-time list value
    var dyn_list = lst # Implicitly materializes the compile-time value to a
    # dynamically-allocated runtime value

    The alias is a compile-time temporary value; to assign it to a runtime variable, Mojo must allocate memory and copy the value. Since this can result in unexpected memory allocations when materializing a memory value like a list, Mojo only allows implicit materializations of memory values if the type is ImplicitlyCopyable, which is a signal that the type should be inexpensive to copy.

    You can use the materialize() function to explicitly materialize a value:

     var dyn_list = materialize[lst]()

Standard library changes

Copyability changes

  • The way copying is modeled in Mojo has been overhauled.

    Previously, Mojo had two traits for modeling copyability:

    • Copyable denoted a type that could be copied implicitly
    • ExplicitlyCopyable denoted a type that could only be copied with an explicit call to a .copy() method.

    The vast majority of types defaulted to implementing Copyable (and therefore were implicitly copyable), and ExplicitlyCopyable was partially phased in but had significant usage limitations.

    Now, the new Copyable trait instead represents a type that can be explicitly copied (using .copy()), and a new ImplicitlyCopyable "marker" trait can be used to opt-in to making a type implicitly copyable as well. This swaps the default behavior from being implicitly copyable to being only explicitly copyable.

    The new ImplicitlyCopyable trait inherits from Copyable, and requires no additional methods. ImplicitlyCopyable is known specially to the compiler. (ImplicitlyCopyable types may also be copied explicitly using .copy().)

    This makes it possible for non-implicitly-copyable types to be used with all standard library functionality, resolving a long-standing issue with Mojo effectively forcing implicit copyability upon all types. This will enable Mojo programs to be more efficient and readable, with fewer performance and correctness issues caused by accidental implicit copies.

    With this change, types that conform to Copyable are no longer implicitly copyable:

    @fieldwise_init
    struct Person(Copyable):
    var name: String

    fn main():
    var p = Person("Connor")
    var p2 = p # ERROR: not implicitly copyable
    var p3 = p.copy() # OK: may be copied explicitly

    To enable a type to be implicitly copyable, declare a conformance to the ImplicitlyCopyable marker trait:

    @fieldwise_init
    struct Point(ImplicitlyCopyable):
    var x: Float32
    var y: Float32

    fn main():
    var p = Point(5, 10)
    var p2 = p # OK: may be implicitly copied
    var p3 = p.copy() # OK: may be explicitly copied

    An additional nuance is that ImplicitlyCopyable may only be synthesized for types whose fields are all themselves ImplicitlyCopyable (and not merely Copyable). If you need to make a type with any non-ImplicitlyCopyable fields support implicit copying, you can declare the conformance to ImplicitlyCopyable, but write the __copyinit__() definition manually:

    struct Container(ImplicitlyCopyable):
    var x: SomeCopyableType
    var y: SomeImplicitlyCopyableType

    fn __copyinit__(out self, existing: Self):
    self.x = existing.x.copy() # Copy field explicitly
    self.y = existing.y

    For more information on copyability, see the section on copy constructors in the Mojo manual.

  • The following standard library types and functions now require only explicit Copyable for their element and argument types, enabling their use with types that are not implicitly copyable: List, Span, InlineArray Optional, Variant, Tuple, Dict, Set, Counter, LinkedList, Deque, and reversed().

    Additionally, the following traits now require explicit Copyable instead of ImplicitlyCopyable: KeyElement, IntervalElement, ConvertibleFromPython

  • The following Mojo standard library types are no longer implicitly copyable: List, Dict, DictEntry, OwnedKwargsDict, Set, LinkedList, Node Counter, CountTuple, BitSet, UnsafeMaybeUninitialized, DLHandle, BenchConfig, BenchmarkInfo, Report, PythonTypeBuilder.

    To create a copy of one of these types, call the .copy() method explicitly:

    var l = List[Int](1, 2, 3)

    # ERROR: Implicit copying of `List` is no longer supported:
    # var l2 = l

    # Instead, perform an explicit copy:
    var l2 = l.copy()

    Alternatively, to transfer ownership, use the ^ transfer sigil:

    var l = List[Int](1, 2, 3)
    var l2 = l^
    # `l` is no longer accessible.
  • Because List and Dict are so widely used, this release stages this in by making implicit copies of these types a warning instead of an error. This will become a hard error in the next release of Mojo.

  • User types that define a custom .copy() method must be updated to move that logic to __copyinit__(). The .copy() method is now provided by a default trait implementation on Copyable that should not be overridden:

    trait Copyable:
    fn __copyinit__(out self, existing: Self, /):
    ...

    fn copy(self) -> Self:
    return Self.__copyinit__(self)
  • The comparison operators (e.g. __eq__() and __le__()) of the SIMD type now return a single Bool instead of a boolean SIMD mask. Moreover, SIMD now has explicit element-wise comparisons that return boolean masks (for example, eq() and le()).

    • This allows SIMD to conform to the EqualityComparable trait, enabling the use of SIMD vectors in sets, as keys to dictionaries, generic search algorithms, etc. Moreover, Scalar now conforms to the Comparable trait, i.e. SIMD conforms to Comparable when the size is 1.
    • As a consequence, SIMD.__bool__() no longer needs to be restricted to scalars, and instead performs an any reduction on the elements of vectors.
  • Non-scalar SIMD constructors no longer allow implicit splatting of Bool values. This could lead to subtle bugs that cannot be caught at compile time, for example:

    fn foo[w: Int](v: SIMD[_, w]) -> SIMD[DType.bool, w]:
    return v == 42 # this silently reduced to a single bool, and then splat

    Similarly to InlineArray, an explicit constructor with the fill keyword-only argument can be used to express the same logic more safely:

    fn foo[w: Int](v: SIMD[_, w]) -> SIMD[DType.bool, w]:
    return SIMD[DType.bool, w](fill=(v == 42)) # highlights the splat logic

    fn bar(Scalar[_]) -> Scalar[DType.bool]:
    # still works, since implicit splatting to a scalar is never ambiguous
    return v == 42
  • Several types that wrap MLIR types have been changed to further encapsulate their behavior, hiding this low-level behavior from non-advanced users.

    • Types that can be constructed from raw MLIR values now require the use of an mlir_value keyword-only argument initializer. Affected types include SIMD and UInt.

    • Types with raw MLIR type fields have had their value fields renamed to _mlir_value. Affected types include: Bool and DType.

  • The SIMD.from_bits() factory method is now a constructor, use SIMD(from_bits=...) instead.

  • DType.index is deprecated in favor of the new DType.int. Moreover, a new DType.uint is added, modeling unsigned integers with widths that match the machine word length.

  • The index() free function now returns an Int, instead of a raw MLIR __mlir_type.index value.

Path and file system changes

  • Added the parts() method to the Path type, for example instead of writing:

    var path = Path("path/to/file")
    var parts = path.path.split(DIR_SEPARATOR)

    you can now write:

    var path = Path("path/to/file")
    var parts = path.parts()
  • Added the name() method to the Path type, which returns the name of the file or directory.

  • Added os.path.realpath() to resolve symbolic links to an absolute path and remove relative path components (., .., etc.). Behaves the same as the Python equivalent function.

Collections and iterators changes

  • Added an iter module which includes the new Iterable trait (and the existing Iterator trait). The module also provides the enumerate(), iter(), map(), next(), and zip() functions. The new generic zip() function replaces the IntTuple-specific zip() function provided in previous releases.

    • Iterable's origin parameter is now named iterable_origin and its mut param is now named iterator_mut to avoid naming collisions.
  • Iterator now has a defaulted bounds() trait method. This returns the lower and upper bounds of the remaining length of the iterator. This can be used for preallocation when building or extending collections from iterators.

  • Added take_items() draining iterator to Dict.

  • Span is now Representable if its elements implement the Representable trait.

  • Add repr() support for List, Deque, Dict, LinkedList, Optional, and Set.

  • InlineArray now automatically detects whether its element types are trivially destructible to not invoke the destructors in its __del__() function. This improves performance for trivially destructible types (such as Int and friends).

  • Similar to above, List now automatically does optimizations based on whether the element types are trivial (copyable, destructible, etc). There is no longer a hint_trivial_type parameter as this is done automatically now.

  • String.splitlines() now returns a List[StringSlice] instead of a List[String]. This avoids unnecessary intermediate allocations.

  • StringSlice.from_utf8() factory method is now a constructor, use StringSlice(from_utf8=...) instead.

  • Span now implements a generic .count() method which can be passed a function that returns a boolean SIMD vector. The function counts how many times it returns True evaluating it in a vectorized manner. This works for any Span[Scalar[D]] e.g. Span[Byte].

  • Optional and OptionalReg can now be composed with Bool in expressions, both at comptime and runtime:

    alias value = Optional[Int](42)

    @parameter
    if CompilationTarget.is_macos() and value:
    print("is macos and value is:", value.value())
  • sort(), LinkedList.pop(), LinkedList.maybe_pop() and Dict.popitem() no longer copy elements, improving performance.

  • The IndexList and DimList types may no longer be implicitly constructed from tuple values. Most existing call sites already used explicit initializer calls (IndexList(...)), so implicit conversions have been removed to ensure uniformity and consistency.

Pointer changes

  • Removed the alignment parameter and static_alignment_cast() method from UnsafePointer. This alignment parameter that was only used by the alloc() static method.

  • Added an alignment keyword argument to UnsafePointer.alloc(). Use this in place of the alignment parameter on the struct.

  • Removed the alignment parameter from Span, similar to UnsafePointer above.

  • The UnsafePointer.init_pointee_explicit_copy() method has been removed. Please use UnsafePointer.init_pointee_copy() instead.

  • UnsafePointer.move_pointee_into() has been deprecated. Please use UnsafePointer.init_pointee_move_from().

    src.move_pointee_into(dst) used a reversed argument order from intuitive LHS = RHS semantics for assignment, effectively RHS -> LHS. The new function fixes this readability issue (dst.init_pointee_move_from(src)), and additionally follows the init_pointee_*() naming convention of the other existing methods for initializer a pointer memory location.

Atomic operations

  • Added os.atomic.fence() for creating atomic memory fences.

      from os.atomic import Atomic, Consistency, fence

    fn decrease_ref_count(ref_count: Atomic[DType.uint64]):
    if atomic.fetch_sub[ordering = Consistency.MONOTONIC](1) == 1:
    fence[Consistency.ACQUIRE]()
    # ...
  • Add a memory ordering parameter to Atomic.load().

System changes

  • Added sys.info.platform_map() for specifying types that can have different values depending on the platform:

    from sys.info import platform_map

    alias EDEADLK = platform_map["EDEADLK", linux = 35, macos = 11]()
  • Renamed a number of functions following functions in sys.info from flatcase to snake_case names:

    Old nameNew name
    alignof()align_of()
    bitwidthof()bit_width_of()
    simdbitwidth()simd_bit_width()
    simdbytewidth()simd_byte_width()
    simdwidthof()simd_width_of()
    sizeof()size_of()

    The old names are deprecated, and will be removed in the next release.

GPU Support

  • Added initial support for programming Apple Silicon GPUs in Mojo. However, MAX graphs are not yet enabled on Apple Silicon GPUs, and many hardware features remain to be enabled.

  • Added support for AMD RX 6900 XT consumer-grade GPU.

  • Added support for AMD RDNA3.5 consumer-grade GPUs in the gfx1150, gfx1151, and gfx1152 architectures. Representative configurations have been added for AMD Radeon 860M, 880M, and 8060S GPUs.

  • Added support for NVIDIA GTX 1080 Ti consumer-grade GPUs.

  • Added support for NVIDIA Tesla P100 datacenter GPUs.

  • Updated layout_tensor() copy related functions to support 2D and 3D threadblock dimensions.

Other standard library changes

  • A new Some utility is introduced to reduce the syntactic load of declaring function arguments of a type that implements a given trait or trait composition. For example, instead of writing

    fn foo[T: Intable, //](x: T) -> Int:
    return x.__int__()

    one can now write:

    fn foo(x: Some[Intable]) -> Int:
    return x.__int__()
  • The compile.reflection.get_type_name() utility now has limited capability to print parametric types, e.g. SIMD[DType.float32, 4] instead of just SIMD. If the parameter is not printable, an <unprintable> placeholder is printed instead. A new qualified_builtins flag also allows users to control the verbosity for the most common (but not all) builtin types.

Kernels changes

  • A fast matmul for SM100 is available in Mojo. Please check it out in matmul_sm100.mojo.

  • Moved the comm module from the standard library (gpu.comm) to the MAX AI kernels library. Any imports that used gpu.comm should be updated to comm, instead.

Tooling changes

  • mojo test now ignores folders with a leading . in the name. This will exclude hidden folders on Unix systems.

  • mojo doc --validate-doc-strings now emits a warning when an fn function is declared to raise an error (raises) and it has no Raises docstring. However, because Mojo automatically treats all def functions as raising functions, we do not enforce Raises docs for def functions (to avoid noisy false positives).

  • Nightly mojo Python wheels are now available. To install everything needed for Mojo development in a Python virtual environment, you can use:

    pip install --pre mojo \
    --index-url https://dl.modular.com/public/nightly/python/simple/

    For more information, see the Mojo install guide.

  • In preparation for a future Mojo 1.0, the mojo and mojo-compiler packages have a 0. prefixed to the version.

❌ Removed

  • The Mojo MLIR C bindings has been removed. This was a private package that was used for early experimentation.

🛠️ Fixed

  • #4695 - Dict.__getitem__() always returns immutable references.
  • #4705 - Wrong mutability inferred for __getitem__() if [] operator is used and __setitem__() is present.
  • #5190 - Mojo compiler crashes for a struct with two constructors taking different keyword-only arguments.
  • #5139 - Crash on malformed initializer.
  • #5183 - Log1p() not working on GPUs.
  • #5105 - Outdated CLAUDE.md docs.
  • #5239 - Contextual type not detected inside an inline if-else.
  • #5305 - Parser Segfaults on LayoutTensor[layout] with no layout in scope.
  • #5260 - Undefined reference to `clock_gettime_nsec_np' when building with -O0.
  • #5307 - Bad error message when getting GPU info for unsupported GPU.
  • Error messages involving types using implicit parameters from auto-parameterized types now include context information to solve a class of incorrect "T != T" error messages common in kernel code.
  • Parameter inference failures now refer to parameters by their user-provided name, rather than complaining about a mysterious "parameter #4".

Special thanks

Special thanks to our community contributors: @AceMouse, @Alex-Mann, @christoph-schlumpf, @cudawarped, @cyrillzadra, @dl-alexandre, @farnoy, @gabrieldemarmiesse, @gryznar, @josiahls, @kyoto7250 @martinvuyk, @mmicu, @msaelices, @mzaks, @rd4com, @Rtosshy, @SasankYadati, @simonyjung, @soraros, and @ThomasMader.