Skip to main content
Version: Nightly

v24.2 (2024-03-28)

🔥 Legendary

  • The Mojo standard library is now open source! Check out the README for everything you need to get started.

  • Structs and other nominal types are now allowed to implicitly conform to traits. A struct implicitly conforms to a trait if it implements all the requirements for the trait. For example, any struct that implements the __str__() method implicitly conforms to Stringable, and is usable with the str() built-in function.

    @value
    struct Foo:
    fn __str__(self) -> String:
    return "foo!"

    fn main():
    print(str(Foo())) # prints 'foo!'

    We still strongly encourage you to explicitly list the traits a struct conforms to when possible:

    @value
    struct Foo(Stringable): ...

    Not only is this useful for documentation and for communicating intentions, but in the future, explicit conformance will be useful for features like default methods and extensions.

  • Mojo's Python interoperability now supports passing keyword arguments to Python functions:

    from python import Python

    def main():
    plt = Python.import_module("matplotlib.pyplot")
    plt.plot((5, 10), (10, 15), color="red")
    plt.show()

Language changes

⭐️ New

  • Mojo now has support for variadic keyword arguments, often referred to as **kwargs. This means you can now declare and call functions like this:

    fn print_nicely(**kwargs: Int) raises:
    for key in kwargs.keys():
    print(key[], "=", kwargs[key[]])

    # prints:
    # `a = 7`
    # `y = 8`
    print_nicely(a=7, y=8)

    For more details (and a list of current limitations), see Variadic keyword arguments in the Mojo manual.

🦋 Changed or removed

  • let declarations now produce a compile time error instead of a warning, our next step in removing let declarations. The compiler still recognizes the let keyword for now in order to produce a good error message, but that will be removed in subsequent releases.

  • Mojo now warns about unused values in both def and fn declarations, instead of completely disabling the warning in defs. It never warns about unused object or PythonObject values, tying the warning to these types instead of the kind of function they are unused in. This will help catch API usage bugs in defs and make imported Python APIs more ergonomic in fns.

  • For the time being, dynamic type values will be disabled in the language. For example, the following will now fail with an error:

    var t = Int  # dynamic type values not allowed

    struct SomeType: ...

    takes_type(SomeType) # dynamic type values not allowed

    We want to take a step back and (re)design type valued variables, existentials, and other dynamic features. This does not affect type valued parameters, so the following works as before:

    alias t = Int  # still 🔥

    struct SomeType: ...

    takes_type[SomeType]() # already 🔥

    >fn uses_trait[T: SomeTrait](value: T): ... # still 🔥
  • The *_ expression in parameter expressions is now required to occur at the end of a positional parameter list, instead of being allowed in the middle.

    # No longer supported
    alias FirstUnbound = SomeStruct[*_, 42]
    alias MidUnbound = SomeStruct[7, *_, 6]
    # Still supported
    alias LastUnbound = SomeStruct[42, *_]

    We narrowed this because we want to encourage type designers to get the order of parameters right, and want to extend *_ to support keyword parameters as well in the future.

Standard library changes

⭐️ New

  • DynamicVector has been renamed to List, and has moved from the collections.vector module to the collections.list module. In addition:

    • You can now construct a List from a variadic number of values. For example:

      var numbers = List[Int](1, 2, 3)
    • List and InlinedFixedVector types now support negative indexing. This means that you can write vec[-1] which is equivalent to vec[len(vec)-1].

    • List.push_back() has been removed. Please use the append() function instead.

  • The print() function now takes sep and end keyword arguments. This means that you can write:

    print("Hello", "Mojo", sep=", ", end="!!!\n") # prints Hello, Mojo!!!

    sep defaults to the empty string and end defaults to "\n".

    Also, the print_no_newline() function has been removed. Please use print(end="") instead.

  • The FloatLiteral type is now an infinite-precision nonmaterializable type. This means you can do compile-time calculations using FloatLiteral without rounding errors. When materialized at runtime, a FloatLiteral value is converted to a Float64.

    # third is an infinite-precision FloatLiteral value
    alias third = 1.0 / 3.0
    # t is a Float64
    var t = third
  • String types all conform to the IntableRaising trait. This means that you can now call int("123") to get the integer 123. If the integer cannot be parsed from the string, then an error is raised.

  • The Tensor type now has argmax() and argmin() functions to compute the position of the max or min value. Note: this should return a Tensor[Int] but currently the output tensor is the same type as the input tensor. This will be fixed in a future release.

  • Added a new collections.OptionalReg type, a register-passable alternative to Optional.

  • The ulp() function has been added to the math module. This allows you to get the units of least precision (or units of last place) of a floating point value.

🦋 Changed

  • The simd_load(), simd_store(), aligned_simd_load(), and aligned_simd_store() methods on DTypePointer, Buffer, and NDBuffer have been merged into a more expressive set of load() and store() methods with keyword-only width and alignment parameters:

    # Doesn't work
    my_simd = my_buffer.simd_load[simd_width](index)
    # Works
    my_simd = my_buffer.load[width=simd_width](index)
    # Doesn't work
    my_buffer.aligned_simd_store[width, alignment](my_simd)
    # Works
    my_buffer.store[width=width, alignment=alignment](my_simd)
  • The EqualityComparable trait now requires the __ne__() method for conformance in addition to the previously required __eq__() method.

  • Many types now declare conformance to EqualityComparable trait.

  • StaticTuple parameter order has changed to StaticTuple[type, size] for consistency with SIMD and similar collection types.

  • The signature of the elementwise() function has been changed. The new order is function, simd_width, and then rank. As a result, the rank parameter can now be inferred and one can call elementwise() without it:

    elementwise[func, simd_width](shape)
  • PythonObject is now register-passable.

  • PythonObject.__iter__() now works correctly on more types of iterable Python objects. Attempting to iterate over non-iterable objects will now raise an exception instead of behaving as if iterating over an empty sequence. __iter__() also now borrows self rather than requiring inout, allowing code like:

    for value in my_dict.values():
    ...

🚚 Moved

  • We took the opportunity to rehome some modules into their correct package as we were going through the process of open-sourcing the Mojo standard library. Specifically, the following are some breaking changes worth calling out. Please update your import statements accordingly.

    • Buffer, NDBuffer, and friends have moved from the memory package into a new buffer package.

      from buffer import Buffer, NDBuffer
    • utils.list, including the Dim and DimList types, has moved to the buffer package.

      from buffer import Dim, DimList
    • The parallel_memcpy() function has moved from the memory package into the buffer package.

      from buffer import parallel_memcpy
    • The rand() and randn() functions from the random package that return a Tensor have moved to the tensor package. Note that the overloads that write to a DTypePointer remain in the random package.

      If you happen to be using both versions in the same source file, you can import them both using the import as syntax:

      from tensor import rand
      from random import rand as rand_dt
    • The trap() function has been renamed to abort(). It also has moved from the debug module to the os module.

      from os import abort
    • The isinf() and isfinite() methods have been moved from math.limits to the math module.

      from math import ininf, isfinite

Tooling changes

⭐️ New

  • Docstring code blocks can now use %# to hide lines of code from documentation generation.

    For example:

    var value = 5
    %# print(value)

    Will generate documentation of the form:

    var value = 5

    Hidden lines are processed as if they were normal code lines during test execution. This allows for writing additional code within a docstring example that is only used to ensure the example is runnable/testable.

  • The Mojo LSP server now allow you to specify additional search paths to use when resolving imported modules in a document. You can specify search paths on the command line, using the -I option, or you can add them to the mojo.lsp.includeDirs setting in the VS Code extension.

Other changes

❌ Removed

  • The __get_address_as_lvalue magic function has been removed. You can now get an LValue from a Pointer or Reference by using the dereference operator ([]):

    var ptr: Pointer[MyRecord]
    ...
    # Doesn't work
    __get_address_as_lvalue(ptr.value) = MyRecord(3, 5)
    # Works
    ptr[] = MyRecord(3, 5)
  • The type parameter for the memcpy function is now automatically inferred. This means that calls to memcpy of the form memcpy[Dtype.xyz](...) will no longer work and the user would have to change the code to memcpy(...).

  • The memcpy() overload that worked on Buffer types has been removed in favor of just overloads for Pointer and DTypePointer:

    # Doesn't work
    memcpy(destBuffer, srcBuffer, count)
    # Works
    memcpy(destBuffer.data, srcBuffer.data, count)
  • The functions max_or_inf(), min_or_neginf() have been removed from math.limit. These functions were only used by the SIMD type.

  • As mentioned previously, the print_no_newline() function has been removed. Please use print(end="") instead.

🛠️ Fixed

  • #1362 - Parameter inference now recursively matches function types.
  • #951 - Functions that were both async and @always_inline incorrectly errored.
  • #1858 - Trait with parametric methods regression.
  • #1892 - Forbid unsupported decorators on traits.
  • #1735 - Trait-typed values are incorrectly considered equal.
  • #1909 - Crash due to nested import in unreachable block.
  • #1921 - Parser crashes binding Reference to lvalue with subtype lifetime.
  • #1945 - Optional[T].or_else() should return T instead of Optional[T].
  • #1940 - Constrain math.copysign to floating point or integral types.
  • #1838 - Variadic print does not work when specifying end=""
  • #1826 - The SIMD.reduce methods correctly handle edge cases where size_out >= size.