Skip to main content
Version: 1.0

v24.1 (2024-02-29)

🔥 Legendary

  • Mojo is now bundled with the MAX platform!

    As such, the Mojo package version now matches the MAX version, which follows a YY.MAJOR.MINOR version scheme. Because this is our first release in 2024, that makes this version 24.1.

  • Mojo debugging support is here! The Mojo VS Code extension includes debugger support. For details, see Debugging in the Mojo Manual.

⭐️ New

  • We now have a Set type in our collections! Set is backed by a Dict, so it has fast add, remove, and in checks, and requires member elements to conform to the KeyElement trait.

    from collections import Set

    var set = Set[Int](1, 2, 3)
    print(len(set)) # 3
    set.add(4)

    for element in set:
    print(element[])

    set -= Set[Int](3, 4, 5)
    print(set == Set[Int](1, 2)) # True
    print(set | Set[Int](0, 1) == Set[Int](0, 1, 2)) # True
    let element = set.pop()
    print(len(set)) # 1
  • Mojo now supports the x in y expression as syntax sugar for y.__contains__(x) as well as x not in y.

  • Mojo now has support for keyword-only arguments and parameters. For example:

    fn my_product(a: Int, b: Int = 1, *, c: Int, d: Int = 2):
    print(a * b * c * d)

    my_product(3, c=5) # prints '30'
    my_product(3, 5, d=7) # error: missing 1 required keyword-only argument: 'c'

    This includes support for declaring signatures that use both variadic and keyword-only arguments/parameters. For example, the following is now possible:

    fn prod_with_offset(*args: Int, offset: Int = 0) -> Int:
    var res = 1
    for i in range(len(args)):
    res *= args[i]
    return res + offset

    print(prod_with_offset(2, 3, 4, 10)) # prints 240
    print(prod_with_offset(2, 3, 4, offset=10)) # prints 34

    Note that variadic keyword-only arguments/parameters (for example, **kwargs) are not supported yet. That is, the following is not allowed:

    fn variadic_kw_only(a: Int, **kwargs): ...

    For more information, see Positional-only and keyword-only arguments in the Mojo Manual.

  • The print() function now accepts a keyword-only argument for the end which is useful for controlling whether a newline is printed or not after printing the elements. By default, end defaults to "\n" as before.

  • The Mojo SDK can now be installed on AWS Graviton instances.

  • A new version of the Mojo Playground is available. The new playground is a simple interactive editor for Mojo code, similar to the Rust Playground or Go Playground. The old JupyterLab based playground will remain online until March 20th.

  • The Mojo LSP server will now generate fixits for populating empty documentation strings:

    fn foo(arg: Int):
    """""" # Unexpected empty documentation string

    Applying the fixit from above will generate:

    fn foo(arg: Int):
    """[summary].

    Args:
    arg: [description].
    """
  • Added new *_ syntax that allows users to explicitly unbind any number of positional parameters. For example:

    struct StructWithDefault[a: Int, b: Int, c: Int = 8, d: Int = 9]: pass

    alias all_unbound = StructWithDefault[*_]
    # equivalent to
    alias all_unbound = StructWithDefault[_, _, _, _]

    alias first_bound = StructWithDefault[5, *_]
    # equivalent to
    alias first_bound = StructWithDefault[5, _, _, _]

    alias last_bound = StructWithDefault[*_, 6]
    # equivalent to
    alias last_bound = StructWithDefault[_, _, _, 6]

    alias mid_unbound = StructWithDefault[3, *_, 4]
    # equivalent to
    alias mid_unbound = StructWithDefault[3, _, _, 4]

    As demonstrated above, this syntax can be used to explicitly unbind an arbitrary number of parameters, at the beginning, at the end, or in the middle of the operand list. Since these unbound parameters must be explicitly specified at some point, default values for these parameters are not applied. For example:

    alias last_bound = StructWithDefault[*_, 6]
    # When using last_bound, you must specify a, b, and c. last_bound
    # doesn't have a default value for `c`.
    var s = last_bound[1, 2, 3]()

    For more information see the Mojo Manual sections on partially-bound types and automatic parameterization of functions.

  • DynamicVector now supports iteration. Iteration values are instances of Reference and require dereferencing:

    var v: DynamicVector[String]()
    v.append("Alice")
    v.append("Bob")
    v.append("Charlie")
    for x in v:
    x[] = str("Hello, ") + x[]
    for x in v:
    print(x[])
  • DynamicVector now has reverse() and extend() methods.

  • The mojo package command now produces compilation agnostic packages. Compilation options such as O0, or --debug-level, are no longer needed or accepted. As a result, packages are now smaller, and extremely portable.

  • Initializers for @register_passable values can (and should!) now be specified with inout self arguments just like memory-only types:

    @register_passable
    struct YourPair:
    var a: Int
    var b: Int
    fn __init__(inout self):
    self.a = 42
    self.b = 17
    fn __copyinit__(inout self, existing: Self):
    self.a = existing.a
    self.b = existing.b

    This form makes the language more consistent, more similar to Python, and easier to implement advanced features for. There is also no performance impact of using this new form: the compiler arranges to automatically return the value in a register without requiring you to worry about it.

    The older -> Self syntax is still supported in this release, but will be removed in a subsequent one, so please migrate your code. One thing to watch out for: a given struct should use one style or the other, mixing some of each won't work well.

  • The inout self initializer form is required for initializers of @register_passable types that may raise errors:

    @register_passable
    struct RaisingCtor:
    fn __init__(inout self) raises:
    raise
  • async functions that may raise errors have been temporarily disabled in this build. The implementation of Mojo async is undergoing a rework 🚧.

  • The standard library slice type has been renamed to Slice, and a slice function has been introduced. This makes Mojo closer to Python and makes the Slice type follow the naming conventions of other types like Int.

  • "Slice" syntax in subscripts is no longer hard coded to the builtin slice type: it now works with any type accepted by a container's __getitem__() method. For example:

    @value
    struct UnusualSlice:
    var a: Int
    var b: Float64
    var c: String

    struct YourContainer:
    fn __getitem__(self, slice: UnusualSlice) -> T: ...

    Given this implementation, you can subscript into an instance of YourContainer like yc[42:3.14:"🔥"] and the three values are passed to the UnusualSlice constructor.

  • The __refitem__() accessor method may now return a Reference instead of having to return an MLIR internal reference type.

  • Added AnyPointer.move_into() method, for moving a value from one pointer memory location to another.

  • Added built-in hex() function, which can be used to format any value whose type implements the Intable trait as a hexadecimal string.

  • PythonObject now implements __is__ and __isnot__ so that you can use expressions of the form x is y and x is not y with PythonObject.

  • PythonObject now conforms to the SizedRaising trait. This means the built-in len() function now works on PythonObject.

  • The os package now contains the stat() and lstat() functions.

  • A new os.path package now allows you to query properties on paths.

  • The os package now has a PathLike trait. A struct conforms to the PathLike trait by implementing the __fspath__() function.

  • The pathlib.Path now has functions to query properties of the path.

  • The listdir() method now exists on pathlib.Path and also exists in the os module to work on PathLike structs. For example, the following sample lists all the directories in the /tmp directory:

    from pathlib import Path

    fn walktree(top: Path, inout files: DynamicVector[Path]):
    try:
    var ls = top.listdir()
    for i in range(len(ls)):
    var child = top / ls[i]
    if child.is_dir():
    walktree(child, files)
    elif child.is_file():
    files.append(child)
    else:
    print("Skipping '" + str(child) + "'")
    except:
    return

    fn main():
    var files = DynamicVector[Path]()

    walktree(Path("/tmp"), files)

    for i in range(len(files)):
    print(files[i])
  • The find(), rfind(), count(), and __contains__() methods now work on string literals. This means that you can write:

    if "Mojo" in "Hello Mojo":
    ...
  • Breakpoints can now be inserted programmatically within the code using the builtin breakpoint() function.

    Note: on Graviton instances, the debugger might not be able to resume after hitting this kind of breakpoint.

  • Added a builtin Boolable trait that describes a type that can be represented as a boolean value. To conform to the trait, a type must implement the __bool__() method.

  • Modules within packages can now use purely relative from imports:

    from . import another_module
  • Trivial types, like MLIR types and function types, can now be bound implicitly to traits that require copy constructors or move constructors, such as Movable, Copyable, and CollectionElement.

  • A new magic __origin_of(expr) call will yield the lifetime of a memory value. We hope and expect that this will eventually be replaced by Reference(expr).lifetime as the parameter system evolves, but this is important in the meantime for use in function signatures.

  • A new magic __type_of(expr) call will yield the type of a value. This allows one to refer to types of other variables. For example:

    fn my_function(x: Int, y: __type_of(x)) -> Int:
    let z: __type_of(x) = y
    return z

🦋 Changed

  • As another step towards removing let declarations we have removed support for let declarations inside the compiler. To ease migration, we parse let declarations as a var declaration so your code won't break. We emit a warning about this, but please switch your code to using var explicitly, because this migration support will be removed in a subsequent update.

    fn test():
    # treated as a var, but please update your code!
    let x = 42 # warning: 'let' is being removed, please use 'var' instead
    x = 9
  • It is no longer possible to explicitly specify implicit argument parameters in automatically parameterized functions. This ability was an oversight and this is now an error:

    fn autoparameterized(x: SIMD):
    pass

    autoparameterized[DType.int32, 1](3) # error: too many parameters
  • vectorize_unroll has been removed, and vectorize now has a parameter named unroll_factor with a default value of 1. Increasing unroll_factor may improve performance at the cost of binary size. See the loop unrolling blog here for more details.

  • The vectorize signatures have changed with the closure func moved to the first parameter:

    vectorize[func, width, unroll_factor = 1](size)
    vectorize[func, width, size, unroll_factor = 1]()

    The doc string has been updated with examples demonstrating the difference between the two signatures.

  • The unroll signatures have changed with the closure func moved to the first parameter:

    unroll[func, unroll_count]()
  • The signature of the NDBuffer and Buffer types have changed. Now, both take the type as the first parameter and no longer require the shape parameter. This allows you to use these types and have sensible defaults. For example:

    NDBuffer[DType.float32, 3]

    is equivalent to

    NDBuffer[DType.float32, 3, DimList.create_unknown[3]()]

    Users can still specify the static shape (if known) to the type:

    NDBuffer[DType.float32, 3, DimList(128, 128, 3)]
  • The error message for missing function arguments is improved: instead of describing the number of arguments (e.g. callee expects at least 3 arguments, but 1 was specified) the missing arguments are now described by name (e.g. missing 2 required positional arguments: 'b', 'c').

  • The CollectionElement trait is now a built-in trait and has been removed from collections.vector.

  • The DynamicVector(capacity: Int) constructor has been changed to take capacity as a keyword-only argument to prevent implicit conversion from Int.

  • Variant.get[T]() now returns a Reference to the value rather than a copy.

  • The String methods tolower() and toupper() have been renamed to str.lower() and str.upper().

  • The ref and mutref identifiers are no longer reserved as Mojo keywords. We originally thought about using those as language sugar for references, but we believe that generic language features combined with the Reference type will provide a good experience without dedicated sugar.

🛠️ Fixed

  • #435 Structs with Self type don't always work.
  • #1540 Crash in register_passable self referencing struct.
  • #1664 - Improve error message when StaticTuple is constructed with a negative size for the number of elements.
  • #1679 - crash on SIMD of zero elements.
  • Various crashes on invalid code: #1230, #1699, #1708
  • #1223 - Crash when parametric function is passed as (runtime) argument. The parser now errors out instead.
  • #1530 - Crash during diagnostic emission for parameter deduction failure.
  • #1538 and #1607 - Crash when returning type value instead of instance of expected type. This is a common mistake and the error now includes a hint to point users to the problem.
  • #1613 - Wrong type name in error for incorrect self argument type in trait method declaration.
  • #1670 - Crash on implicit conversion in a global variable declaration.
  • #1741 - Mojo documentation generation doesn't show inout/owned on variadic arguments.
  • #1621 - VS Code does not highlight raises and capturing in functional type expressions.
  • #1617 - VS Code does not highlight fn in specific contexts.
  • #1740 - LSP shows unrelated info when hovering over a struct.
  • #1238 - File shadows Mojo package path.
  • #1429 - Crash when using nested import statement.
  • #1322 - Crash when missing types in variadic argument.
  • #1314 - Typecheck error when binding alias to parametric function with default argument.
  • #1248 - Crash when importing from file the same name as another file in the search path.
  • #1354 - Crash when importing from local package.
  • #1488 - Crash when setting generic element field.
  • #1476 - Crash in interpreter when calling functions in parameter context.
  • #1537 - Crash when copying parameter value.
  • #1546 - Modify nested vector element crashes parser.
  • #1558 - Invalid import causes parser to crash.
  • #1562 - Crash when calling parametric type member function.
  • #1577 - Crash when using unresolved package as a variable.
  • #1579 - Member access into type instances causes a crash.
  • #1602 - Interpreter failure when constructing strings at compile time.
  • #1696 - Fixed an issue that caused syntax highlighting to occasionally fail.
  • #1549 - Fixed an issue when the shift amount is out of range in SIMD.shift_left and SIMD.shift_right.