Skip to main content
Version: 1.0

v25.1 (2025-02-13)

✨ Highlights

  • The legacy borrowed/inout keywords and -> T as foo syntax are deprecated and now generate a compiler warning. Please move to read/mut/out argument syntax instead. See Argument conventions in the Mojo Manual for more information.

  • The bool(), float(), int(), and str() functions are deprecated and generate compiler warnings. Please use the Bool(), Float64(), Int(), and String() constructors instead. See Standard library changes for more details.

  • The standard library has many changes related to strings. The new Char struct represents a single Unicode character, and includes several methods for categorizing character types. When iterating over the characters of a String with a for loop, you now should use the String.chars() method to provide an iterator of Char values or the String.char_slices() method to provide an iterator of StringSlice instances for each character. StringRef has been removed in favor of StringSlice. And various functionality has moved from String and StringLiteral to the more general StringSlice type. See Standard library changes for more details.

  • You can now use SIMD constructors to cast existing SIMD values (including Scalar values) to a different type, though you can still use the SIMD.cast() method to infer the size of the new vector. See Standard library changes for more details.

Language changes

  • The legacy borrowed/inout keywords and -> T as foo syntax now generate a warning. Please move to read/mut/out argument syntax instead. See Argument conventions in the Mojo Manual for more information.

  • Initializers are now treated as static methods that return an instance of Self. This means the out argument of an initializer is now treated the same as any other function result or out argument. This is generally invisible, except that patterns like instance.__init__() and x.__copyinit__(y) no longer work. Simply replace them with instance = T() and x = y respectively.

  • The @value decorator now additionally derives an implementation of the ExplicitlyCopyable trait. This will ease the transition to explicit copyability requirements by default in the Mojo collection types.

  • Indexing into a homogenous tuple now produces the consistent element type without needing a rebind:

      var x = (1, 2, 3, 3, 4)
    var y : Int = x[idx] # Just works!
  • You can now overload positional arguments with a keyword-only argument, and keyword-only arguments with different names:

      struct OverloadedKwArgs:
    var val: Int

    fn __init__(out self, single: Int):
    self.val = single

    fn __init__(out self, *, double: Int):
    self.val = double * 2

    fn __init__(out self, *, triple: Int):
    self.val = triple * 3

    fn main():
    OverloadedKwArgs(1) # val=1
    OverloadedKwArgs(double=1) # val=2
    OverloadedKwArgs(triple=2) # val=6

    This also works with indexing operations:

    struct OverloadedKwArgs:
    var vals: List[Int]

    fn __init__(out self):
    self.vals = List[Int](0, 1, 2)

    fn __getitem__(self, idx: Int) -> Int:
    return self.vals[idx]

    fn __getitem__(self, *, idx2: Int) -> Int:
    return self.vals[idx2 * 2]

    fn __setitem__(mut self, idx: Int, val: Int):
    self.vals[idx] = val

    fn __setitem__(mut self, val: Int, *, idx2: Int):
    self.vals[idx2 * 2] = val


    fn main():
    var x = OverloadedKwArgs()
    print(x[1]) # 1
    print(x[idx2=1]) # 2

    x[1] = 42
    x[idx2=1] = 84

    print(x[1]) # 42
    print(x[idx2=1]) # 84
  • The __disable_del x operation has been tightened up to treat all fields of x as consumed by the point of the deletion, so it should be used after all the subfields are transferred or otherwise consumed (for example, at the end of the function), not before uses of the fields.

GPU programming

  • The new gpu package provides low-level programming constructs for working with GPUs. The Mojo gpu APIs allow you to manually manage interaction between the CPU host and GPU device, manage memory between devices, synchronize threads, and more. Currently the best way to use these APIs is from inside a MAX custom operation.

    The following code example shows a GPU kernel written in Mojo:

    from max.tensor import ManagedTensorSlice
    from gpu import thread_idx, block_dim, block_idx

    fn gpu_add_kernel(out: ManagedTensorSlice, x: ManagedTensorSlice[out.type, out.rank]):
    tid_x = thread_idx.x + block_dim.x * block_idx.x
    tid_y = thread_idx.y + block_dim.y * block_dim.y
    if tid_x < x.dim_size(0) and tid_y < x.dim_size(1):
    out[tid_x, tid_y] = x[tid_x, tid_y] + 1

    The example above includes only the actual kernel code that’s run on the GPU, not the code to define a custom operation or launch the kernel. For more complete examples, see vector_addition.mojo and top_k.mojo.

  • The layout package includes APIs for working with layouts, which describe the organization of a tensor (for example, row-major or column-major layout), and the LayoutTensor type, which represents a tensor with a specified layout. The layout package can be used to build efficient tensor operations that run on a GPU.

    We’ll continue adding code examples and documentation for the gpu and layout packages in future releases.

Standard library changes

  • The builtin functions for converting values to different types have been deprecated for actual constructors:

    BeforeAfter
    bool()Bool()
    float()Float64()
    int()Int()
    str()String()

    These functions were a workaround before Mojo had a way to distinguish between implicit and explicit constructors. For this release you'll get a deprecation warning, and in the next release they'll become compiler errors. You can quickly update your code by doing a Match Case and Match Whole Word search and replace for int( to Int( etc.

  • String and friends:

    • Added Char for representing and storing single Unicode characters.

    • StringRef has been removed in favor of StringSlice. The two types are ABI compatible, and for the exact same behavior one can use StaticString, which is an alias to StringSlice[StaticConstantOrigin].

    • Various functionality has moved from String and StringLiteral to the more general StringSlice type.

    • Added StringSlice.from_utf8() factory method, for validated construction of a StringSlice from a buffer containing UTF-8 encoded data. This method will raise if the buffer contents are not valid UTF-8.

    • Added StringSlice.chars() which returns an iterator over Chars. This is a compliant UTF-8 decoder that returns each Unicode codepoint encoded in the string.

    • Added StringSlice.__getitem__(Slice) which returns a substring. Only step sizes of 1 are supported.

    • Several standard library functions have been changed to take StringSlice instead of String. This generalizes them to be used for any appropriately encoded string in memory, without requiring that the string be heap allocated. This includes: ascii(), atol(), atof(), b16decode(), b16encode(), b64decode(), b64encode(), and ord().

    • Added new String.chars() and String.char_slices() iterator methods, and deprecated the existing String.__iter__() method.

      Different use-cases may prefer iterating over the Chars encoded in a string, or iterating over subslices containing single characters. Neither iteration semantics is an obvious default, so the existing __iter__() method has been deprecated in favor of writing explicit iteration methods for the time being.

      Code of the form:

      var s: String  = ...
      for c in s:
      # ...

      can be migrated to using the .char_slices() method:

      var s: String = ...
      for c in s.char_slices():
      # ...
    • Added StringSlice.char_length() method, to pair with the existing StringSlice.byte_length() method.

    • The String.__len__() and StringSlice.__len__() methods now return the length of the string in bytes.

      Previously, these methods were documented to note that they would eventually return a length in Unicode codepoints. They have been changed to guarantee a length in bytes, since the length in bytes is how they are most often used today (for example, as bounds to low-level memory manipulation logic). Additionally, length in codepoints is a more specialized notion of string length that is rarely the correct metric.

      Users that know they need the length in codepoints can use the str.char_length() method, or len(str.chars()).

    • StringSlice now implements Representable, and that implementation is now used by String.__repr__() and StringLiteral.__repr__().

    • StringSlice now implements EqualityComparable.

      Up until now, StringSlice has implemented a more general __eq__() and __ne__() comparison with StringSlice types that had arbitrary other origins. However, to satisfy EqualityComparable, StringSlice now also has narrower comparison methods that support comparing only with another StringSlice with the exact same origin.

    • The String.write() static method has moved to a String() constructor, and is now buffered. Instead of doing:

      var msg = "my message " + String(x) + " " + String(y) + " " + String(z)

      Which reallocates the String you should do:

      var msg = String("my message", x, y, z, sep=" ")

      Which is cleaner, and buffers to the stack so the String is allocated only once.

    • You can now pass any Writer to write_buffered():

      from utils.write import write_buffered

      var string = String("existing string")
      write_buffered(string, 42, 42.4, True, sep=" ")

      This writes to a buffer on the stack before reallocating the String.

  • Collections:

    • A new LinkedList type has been added to the standard library.

    • Added Optional.copied() for constructing an owned Optional[T] from an Optional[Pointer[T]] by copying the pointee value.

    • Added Dict.get_ptr() which returns an Optional[Pointer[V]]. If the given key is present in the dictionary, the optional will hold a pointer to the value. Otherwise, an empty optional is returned.

    • Added new List.extend() overloads taking SIMD and Span. These enable growing a List[Scalar[..]] by copying the elements of a SIMD vector or Span[Scalar[..]], simplifying the writing of some optimized SIMD-aware functionality.

  • UnsafePointer changes:

    • UnsafePointer's bitcast() method has now been split into bitcast() for changing the type, origin_cast() for changing mutability, static_alignment_cast() for changing alignment, and address_space_cast() for changing the address space.

    • UnsafePointer is now parameterized on mutability. Previously, UnsafePointer could only represent mutable pointers.

      The new mut parameter can be used to restrict an UnsafePointer to a specific mutability: UnsafePointer[T, mut=False] represents a pointer to an immutable T value. This is analogous to a const * pointer in C++.

    • UnsafePointer.address_of() will now infer the origin and mutability of the resulting pointer from the argument. For example:

      var local = 10
      # Constructs a mutable pointer, because `local` is a mutable memory location
      var ptr = UnsafePointer.address_of(local)

      To force the construction of an immutable pointer to an otherwise mutable memory location, use a cast:

      var local = 10
      # Cast the mutable pointer to be immutable.
      var ptr = UnsafePointer.address_of(local).origin_cast[mut=False]()
    • The unsafe_ptr() method on several standard library collection types have been updated to use parametric mutability: they will return an UnsafePointer whose mutability is inherited from the mutability of the ref self of the receiver at the call site. For example, ptr1 will be immutable, while ptr2 will be mutable:

      fn take_lists(read list1: List[Int], mut list2: List[Int]):
      # Immutable pointer, since receiver is immutable `read` reference
      var ptr1 = list1.unsafe_ptr()

      # Mutable pointer, since receiver is mutable `mut` reference
      var ptr2 = list2.unsafe_ptr()
  • New and updated traits:

    • The ExplicitlyCopyable trait has changed to require a fn copy(self) -> Self method. Previously, an initializer with the signature fn __init__(out self, *, other: Self) had been required by ExplicitlyCopyable.

      This improves the "greppability" and at-a-glance readability when a programmer is looking for places in their code that may be performing copies.

    • The IntLike trait has been removed and its functionality incorporated into the Indexer trait. This enables SIMD scalar integer types and UInt to be used for indexing into all of the collection types, as well as optimizing away normalization checks for UInt indexing.

    • The ImplicitlyIntable trait has been added, allowing types to be implicitly converted to an Int by implementing the __as_int__() method:

      @value
      struct Foo(ImplicitlyIntable):
      var i: Int

      fn __as_int__(self) -> Int:
      return self.i
  • You can now cast SIMD types using constructors:

    var val = Int8(42)
    var cast = Int32(val)

    It also works when passing a scalar type to larger vector size:

    var vector = SIMD[DType.int64, 4](cast) # [42, 42, 42, 42]

    For values other than scalars the size of the SIMD vector needs to be equal:

    var float_vector = SIMD[DType.float64, 4](vector)

    SIMD.cast() still exists to infer the size of new vector:

    var inferred_size = float_vector.cast[DType.uint64]() # [42, 42, 42, 42]
  • Added SIMD.from_bytes() and SIMD.as_bytes() to convert a list of bytes to a list of scalars and vice versa, accepting the endianness as an argument. Similar to Python int.from_bytes() and int.to_bytes() functions.

  • You can now use max() and min() with variadic number of arguments.

  • bit_ceil() has been renamed to next_power_of_two(), and bit_floor() to prev_power_of_two(). This is to improve readability and clarity in their use.

  • Added a new boolean validate parameter to b64decode().

  • The b64encode() overload that previously took a List has been changed to take a Span.

  • Removed the @implicit decorator from some standard library initializer methods that perform allocation. This reduces places where Mojo code could implicitly allocate where the user may not be aware.

    Removed @implicit from:

    • String.__init__(out self, StringSlice)
    • List.__init__(out self, owned *values: T)
    • List.__init__(out self, span: Span[T])
  • Added more aliases in sys.ffi to round out the usual needs for FFI bindings.

Tooling changes

  • mblack (aka mojo format) no longer formats non-Mojo files. This prevents unexpected formatting of Python files.

  • Full struct signature information is now exposed in the documentation generator, and in the symbol outline and hover markdown via the Mojo Language Server.

  • The env_get_dtype() function has been added to the sys.param_env module. This allows you to get the value of a DType from the param environment.

❌ Removed

  • StringRef has been removed. Use StringSlice instead.

    • Changed sys.argv() to return list of StringSlice.

    • Added explicit Path() constructor from StringSlice.

  • The Tuple.get[i, T]() method has been removed. Please use tup[i] or rebind[T](tup[i]) as needed instead.

  • StringableCollectionElement is deprecated. Use WritableCollectionElement instead, which still allows you to construct a String, but can avoid intermediate allocations.

  • The IntLike trait has been removed and its functionality incorporated into the Indexer trait.

  • The Type{field1: 42, field2: 17} syntax for direct initializing register passable types has been removed. This was legacy syntax - to upgrade your code, add the @value decorator to your struct to get a fieldwise initializer and use Type(field1=42, field2 = 17) instead.

🛠️ Fixed

  • The Mojo Kernel for Jupyter Notebooks is working again on nightly releases.

  • The command mojo debug --vscode now sets the current working directory properly.

  • Issue #3796 - Compiler crash handling for-else statement.

  • Issue #3540 - Using named output slot breaks trait conformance

  • Issue #3617 - Can't generate the constructors for a type wrapping !lit.ref

  • The Mojo Language Server doesn't crash anymore on empty __init__.mojo files. Issue #3826.

  • Issue #3935 - Confusing OOM error when using Tuple.get() incorrectly.

  • Issue #3955 - Unexpected copy behavior with def arguments in loops

  • Issue #3960 - Infinite for loop