Mojo 1.2 landed on October 22. If you’re running any project on 1.1 or older, you have four categories of breaking changes waiting for you, and at least one of them will stop your build cold. The headline feature is pattern matching — genuinely good, worth the upgrade. But sort out the breaks first.
What Breaks
1. async and await Are Now __async and __await
This one hits any codebase using async code. Modular renamed async/await to __async/__await to signal that the async system is still unstable. The old spellings compile but emit a warning per call site. In a codebase with 50 async functions, that’s 50 warnings drowning your build output.
The fix is a global find-replace:
# Before
async fn fetch_data() -> String:
var result = await http_get(url)
return result
# After
__async fn fetch_data() -> String:
var result = __await http_get(url)
return result
The double-underscore convention is Mojo’s way of marking unstable features — honest about the async system’s incomplete state. Do the rename now so warnings don’t mask real issues elsewhere in your build.
2. StringSpan Is Now Immutable — MutStringSpan Is Gone
MutStringSpan, MutStringSlice, and the mut parameter on StringSpan are removed entirely. This is a compile error, not a warning — your build stops here if you’re mutating string bytes through a span. The replacement is Span[Byte] or Array[Byte] for any mutable byte access. StringSpan is a read-only view, full stop.
3. std.hashlib Is Now std.hash — And the Byte API Changed
Two changes in one: the module path and the byte-hashing function signature.
# Import — update the path
from std.hash import Hasher, default_hasher # was std.hashlib
# Byte hashing — old API deprecated
var h = hash_bytes(text.as_bytes()) # takes ImmSpan[Byte]
# was: hash(text.ptr(), text.byte_length())
The pointer-length hash() overload is deprecated but still compiles with a warning. hash(my_list) for Hashable types is unchanged — only the raw-bytes overload was replaced.
4. FileDescriptor → RawFileDescriptor
Straightforward rename: FileDescriptor is now RawFileDescriptor, and the constructor takes c_int explicitly. Low surface area unless you’re doing systems-level I/O. Two additional minor breaks: empty tuple () now always denotes a value, not a type — use Tuple[] for the type. And String.isspace() is renamed to is_ascii_whitespace().
Pattern Matching: The Reason to Upgrade
Mojo 1.2 adds match/case with the full set: literals, tuples, structs, guards, wildcards, and compile-time dispatch via comptime match. The compiler enforces exhaustive coverage for structs — no silent fall-throughs. Here’s what replaces a typical if/elif chain:
# Before (Mojo 1.1)
fn handle(code: Int) -> String:
if code == 200:
return "OK"
elif code == 301 or code == 302:
return "Redirect"
elif code >= 500:
return "Server error"
else:
return "Unknown: " + str(code)
# After (Mojo 1.2)
fn handle(code: Int) -> String:
match code:
case 200: return "OK"
case 301 | 302: return "Redirect"
case c if c >= 500: return "Server error"
case _: return "Unknown: " + str(c)
You also get if case and while case for optional unwrapping, and list destructuring:
if case .Some(item) = get_next():
process(item)
var [first, *rest] = values
If you’ve been maintaining long if/elif chains for state machines, protocol dispatch, or error handling in Mojo, this is the feature you’ve been waiting for. The official changelog has the full pattern matching syntax reference.
GPU Math Now Works Without Platform-Specific Code
Over 20 std.math functions — atan, tan, cbrt, erfc, lgamma, log10, and the Bessel functions — now have native Mojo GPU implementations. Before this release, they fell back to the system C math library, which doesn’t execute on GPU hardware. That meant either reimplementing functions or reaching for platform-specific CUDA/HIP equivalents.
One caveat: numerical results can differ by ±1 ULP depending on hardware. Float64 log improved from roughly 20 million ULP error to ≤1 ULP. If your GPU kernels have golden test outputs, expect some diffs — that’s a worthwhile trade. The Linuxiac write-up has more detail on the GPU math additions.
Performance Numbers
- nn package compile time: 98s → 61s (38% reduction)
- String padding operations: up to 86x faster via single-allocation rewrite
- startswith() / endswith(): now O(prefix length) instead of O(string length)
- GPU kernel compilation on Linux: 10–13% faster via jemalloc allocator
The compile-time win matters most for teams running CI on large Mojo codebases. Recovering 37 seconds per build run compounds quickly across a day of commits.
Upgrade Checklist
- Find
async fn→ rename to__async fn - Find
await(with space) → rename to__await - Find
MutStringSpan/MutStringSlice→ migrate toSpan[Byte] - Find
from std.hashlib→ change tofrom std.hash - Find
hash(ptr, len)calls → migrate tohash_bytes() - Find
FileDescriptor(→ change toRawFileDescriptor( - Find
String.isspace()→ rename tois_ascii_whitespace() - Run
mojo buildwith warnings treated as errors to catch stragglers - Check snapshot/golden tests if your code uses GPU math — 1 ULP diffs are expected
Most of these are mechanical find-replaces. A codebase with no async usage, no raw string mutation, and no direct file descriptor access can upgrade in under ten minutes. Check the full Mojo 1.2.0 release notes before assuming you’re clean — the changelog lists every removed symbol.













