Ante's new borrow checker swaps lifetimes for places

Jake Fecher, writing on the Ante language blog on 2026-10-19, lays out a new borrow checking design for Ante, a low-level functional language built on ownership and borrowing, similar to Rust. A core goal of Ante is a more lenient borrow checker than Rust's. In particular, it relaxes Rust's rule that a reference can be either aliased or mutated, but never both at once. His opening example is a Rust tuple: take a reference to the first element, then assign a new tuple to the variable, and Rust rejects it with error E0506 even though nothing dangles. Fecher wants such code to compile while still ruling out dangling pointers, and wants the check done entirely statically in the type system.
The design has changed substantially since the first post on the site. The new version blends many elements from group borrowing with the author's own ideas. He argues shared mutability is worth having for four reasons: fewer borrow checker errors (easing the learning curve and speeding iteration), more design patterns such as the observer pattern and directly represented cyclic types, fewer workarounds like forced indices into vectors, and easier interop with languages that also have shared mutability. Ante is meant to be opt-in to ownership to some extent, so that by default it feels more like C# than Rust. He calls that an aspirational goal.
In an aside, Fecher explains what was wrong with the old rules. Ante's ref and mut references were in a way analogues of Rust's &Cell
The core idea is shape stability. Writing to the first field of a pair of Strings while holding a reference to it is safe, because the pair and its Strings are unboxed and no mutation can move what the reference points at. Holding a reference to a Vec element and then clearing the vector is not safe, because mutating the vector can change its data pointer and leave the reference dangling. If a type's shape is stable, no mutation can invalidate a previously valid reference into it. Fecher wants references to remember whether they point at something stable or unstable, so uniqueness can be dropped for a more accurate restriction that rejects fewer valid programs.
The first piece is places instead of lifetimes. A lifetime is an arbitrary symbol for an abstract region of code. A place is the exact set of things a reference may borrow from. A reference to x has place 'x, and one that may borrow from x or y has place '(x, y). Places can be paths, such as 'z.bar for a field, and can be anonymous, such as for a reference to a temporary. Borrow checking then becomes simple at its core: whenever a value is used, error if any place it refers to has already been moved. Fecher says there is a little more to it, but the core can be implemented in a few lines of code.
The second piece is unstable places, marked with !. An unstable place like a! is invalidated by any mutation to the place a. Mutating a variable with place a! only invalidates variables with place a!!, so as a hypothetical example, 20 variables sharing place a! can all be mutated simultaneously. This lets Ante accept a case Rust rejects: taking first_mut and last_mut of the same vector. In Rust the compiler refuses two mutable borrows of *v, though Fecher says this code should be valid even if the first element is also the last. In Ante, Vec.first_mut and Vec.last_mut return a mut 'v! t, an unstable reference inside v. Mutating first would only invalidate things with place v!!, and nothing has that place, so both references can be used. Whether a reference is unstable is inferred in practice and does not always need to be a manual choice. For example, any reference through a tagged union or an element of a collection type is unstable.
Loops and branches follow ownership rules: an error is issued if a variable declared outside a loop is invalidated inside it. His example holds a reference to a vector element, then clears the vector in a for loop, and the compiler flags it. A closure that invalidates part of its captured environment is treated like a FnOnce in Rust terms and can be called only once. Effect handlers have more complex rules for invalidating unstable places, which Fecher says are subject to change and which he thinks are a bit hacky. Interactions of handle with unstable places are, at the time of writing, still unimplemented.
Some functions are valid only if parameters do not alias, so the compiler still needs an exclusivity rule. Whenever a mutation occurs, the compiler issues a Mutate 'p effect tagging the function with the places it may mutate. At call sites, each such place must be distinct from the other places instantiated. A caller passing the same mutable vector for two parameters that cannot alias gets an error. The mut type itself still allows arbitrary aliasing; the check happens only when an actual mutation occurs. The article then begins a Use vs Mention section: code that merely mentions mutability is not subject to uniqueness constraints, so a whole mut Context can be passed to a function that only mutates 'ctx.field.
Key facts
- Jake Fecher's 2026-10-19 post describes a new borrow checker for Ante, a low-level functional language with ownership and borrowing similar to Rust, aimed at being more lenient than Rust's.
- References carry places, the exact set of things they may borrow from, instead of lifetimes; borrow checking at its core errors when a value is used and any place it refers to has already been moved.
- An unstable place such as a! is invalidated by any mutation to a, which lets Ante accept taking first_mut and last_mut of one vector, code Rust rejects.
- The old design needed four reference types (ref, mut, imm, uniq) and a Distinct a b trait that made adding a private field to a public struct a breaking change.
- Effect handler (handle) interactions with unstable places are still unimplemented, and the author calls their current rules a bit hacky and subject to change.
Why it matters
Rust's rule that a reference may be aliased or mutated but not both rejects some code that is in fact safe, such as borrowing the first and last element of one vector mutably. Fecher's design tries to separate safe shared mutability from unsafe by asking whether a type's shape is stable, and to do it statically. If it works, he argues it would mean fewer borrow checker errors, direct observer patterns and cyclic types, and easier interop with languages that share mutable state. The ideas are blended from group borrowing plus his own, and the post presents them as the replacement for Ante's earlier approach.
Who it affects
Mainly people interested in language design and borrow checking, and followers of Ante itself. Fecher frames the lenience as helping both new users, who hit fewer errors, and experienced ones, who iterate faster. He also says Ante is meant to feel more like C# than Rust by default, which is an aspirational goal. Library authors are named in the old design: adding a private field to a public struct could break callers.
How to use it
Nothing to install or adopt from this post; it explains design and shows Ante code. The examples show the intended style: Vec.first_mut and Vec.last_mut return unstable references of type mut 'v! t, and the compiler infers instability for references through tagged unions or collection elements, so users do not always pick it by hand. Readers who want the details on effect handlers can follow the rules document the author links, which he says may change.
How solid is it
This is a first-person design write-up by the language's author, not an independent evaluation. It gives no benchmarks, no user studies and no claim of a formal soundness proof. Its arguments rest on worked code examples and memory-layout diagrams. The excerpt used here ends partway through the Use vs Mention section, so any conclusion and later examples are not covered. Only a few points are marked as not done: handle interactions with unstable places are unimplemented at the time of writing.
Risks and caveats
Fecher himself says effect handler rules are subject to change and that he thinks they are a bit hacky. The claim that cyclic mutability is supported safely is the author's own. The exclusivity rule means some functions are valid only if their arguments do not alias, and callers that pass the same value twice get a compile error. Fecher acknowledges that many Rustaceans are skeptical of shared mutability, and he does not claim the lenience is free: it moves checking to places and to Mutate effects at call sites.
“a place is the exact set of things a reference may borrow from”
— Jake Fecher, Ante blog