Pangram verdict · v3.3
We believe that this entire text is human-written.
AI likelihood · overall
HumanArticle text · 1,658 words · 1 segments analyzed
Oct 11, 2026 — This post is about Valen's new flexible borrow checker. Welcome! You all know me, I love exploring new memory safety approaches and blending them together in weird ways. It's part of my eternal quest to find the memory safety holy grail: something as powerful as Rust's borrow checking, yet something as simple and flexible as reference counting or garbage collection. 0 1 I've long suspected that to find the holy grail, we'd have to solve three memory-safety challenges: Can we make a more relaxed borrow checker? 2 Can we make a borrow checker read more complex data? 3 4 Can we make those two systems work together at the same time? Vague and arcane, I know! Further below I'll explain what that all means, and how Valen might have found a solution for them. 5 This post mainly focuses on the first piece: Valen's more relaxed borrow checker. So in this post, I'll talk about what it is, what it can do, and how it works. In this article, I take some liberties with syntactic sugar for clarity: auto-deref, directly indexing a struct (my_vec[0]), and inferring types from groups (entity in world.entities[]) are coming in November. Aside from those syntactic adjustments, the borrow checker works for everything we talk about. 6 And, along the way, I'll explain some of the weirder tidbits, like: How a compiler can remember where a reference is pointing. How Valen has one kind of borrow reference, as opposed to the two that other borrow checkers have. How Valen's borrow checker is shaped to help it call into Rust code. Also, this article is gloriously long and has a lot of context, so I'll let you know when to skip ahead. Let's dive in! Group borrowing: a near miss, and huge potential (This is just backstory that I love telling, feel free to skip this section!) Valen's approach is built on the "Group Borrowing" proposal which was designed by my friend Nick Smith. It's built around the core concept that the compiler remembers where a reference points to, (I'll explain this later) and it uses that information to check that programs are memory-safe. The design's life had a rough start; it was almost buried in the flows of time. It was originally designed with Mojo in mind, but alas, despite my best efforts I couldn't convince the higher-ups that we should use it. 7 Group borrowing was dead before it had the chance to succeed. But I knew it had massive potential, if it could just make it into the world somehow. So we spent months sharpening the explanation and figuring out its benefits, and wrote a post 8 on it last year, hoping other languages would pick it up. And they did! It spread far and wide. 9 Google's Carbon language pivoted completely to group borrowing (and even credited our article, which was nice of them!) Ante started working it into their language, see Shared Mutability and Stability. Plecra's exclusion typing is redesigning the Rust borrow checker with concepts from group borrowing and GhostCell. 10 Zeta blended group borrowing with disjointness 11 to factor run-time facts into compile-time memory safety. Fang is working it into their designs, see Cyclic References in Fang. And now, my Valen combines group borrowing with Vale's 12 approach, and makes it interop with Rust code too. Most of these languages are circling the same sort of challenge: how to make a more flexible borrow checker. For example, Valen wants to make a borrow checker that's flexible enough to work with reference counting and generational references, and Carbon wants to make a borrow checker flexible enough to work with C++ code. This is difficult, because there's been a fundamental conflict between borrow checking and other approaches. It can be summed up like this: 13 Borrow checking has the "shared-xor-mutable" restriction: if you hold a reference to an object, nobody else can change the object. Other approaches want to allow holding a reference to an object while letting others change it. 14 15 You're probably thinking, "There's definitely no way to resolve this." It does seem that way! But group borrowing actually resolves the conflict, by relaxing "shared-xor-mutable" to "no use-after-free". That was cryptic and won't make any sense, but it will later when I explain how group borrowing works. So let's see what group borrowing can do, and then I'll explain how it works. What group borrowing can do Group borrowing gives a program memory safety without run-time cost. 16 It catches use-after-free problems at compile time, and it does it in a way that has less restrictions than previous approaches. Here's an example where a use-after-free error is caught at compile time: struct Entity { hp int; } struct World { entities Vec<Entity>; } func main() int { let world = World(Vec<Entity>.new()); world.entities.push(Entity(42)); let first_ref = &world.entities[0]; world.entities = Vec<Entity>.new(); // Compile error: Used a borrow after invalidated return first_ref.hp; } It's an unusually flexible approach. Usually, compilers have trouble with the below program, but this approach understands that it's safe: 17 func main() int { let list = Vec<int>.new(); // Make two refs to the list let ref_a = &list; let ref_b = &list; // Mutate the list through both refs ref_a.push(42); ref_b.push(73); return 42; } Group borrowing accepts a lot of patterns that are normally hard for borrow checkers, such as: Having multiple local variables that point to the same object, and write through any of them (like above). Take a parameter pointing at an object, and another parameter pointing somewhere inside that object, and write through only the latter (we'll see this in the next section). Having multiple function parameters that point to the same object, and write through any of them. 18 Make a "rollback" struct, that will change an object when it goes out of scope, like the ScopeGuard pattern. 19 20 Having multiple structs that have mutable references to a common subsystem. 21 Plus a lot more that I'll explain further below. 22 These are patterns we love from C++, but no language has figured out how to do all of these in a memory-safe way at compile time. Valen's Memory Safety: A New Kind of Borrow Checking Group borrowing: a near miss, and huge potential What group borrowing can do A more interesting example How Valen's borrow checker works Single Ownership Paths Every reference knows what path it points to Using paths for memory safety Function signatures communicate invalidations A twist on group borrowing Mutable-in-Immutable Temporary Uniqueness Wildcard Descendant Paths How this all fits in That's all for now Appendix: The type-stability exception 0 This quest led to a lot of interesting experiments with constraint references, linear types, reference counting, and a lot more. 1 0% of this article was written by AI. More thoughts here. My stance: if you want someone to take the time to read something, take the time to write it by hand. Thanks for reading =) 2 Or more specifically, "can we make a borrow checker that supports multiple read-write borrow references to a single object?" This post explains that in full, so keep reading! 3 I'll explain this more below, but by "more complex data" I mean entire graphs of data, where objects can freely have references to other objects without restriction, like you see in C++, Java, etc. as opposed to something like Rust or Fortran where things generally can't have references to each other. So when I say "read more complex data", I mean: how can we have immutable borrow references pointing at that kind of arbitrary graph of data? (Note, Rust structs can have references to other Rust structs, but only temporarily. This is why ECS is such a popular choice in Rust games. That's why I still count Rust as more on the Fortran side of things.) 4 In short, this one is answered by Vale's blend of generational references and region borrowing; its immutable region borrowing lets the compiler track all pre-existing data as a temporarily frozen "region" of data, that we can have borrow references into. 5 For now, I'll just hint that Valen might have solved these by blending Vale's approach, a certain thought experiment from 2023, Nick Smith's Group Borrowing approach, and a few twists to make things work together harmoniously. 6 Specifically, in today's compiler, one needs to manually dereference, index into arrays directly like my_vec.data[0] instead of the struct, and put types on all parameters like entity &Entity in world.entities[]. 7 Such is life when working on someone else's language! 8 This post you're reading right now is a much better explanation of group borrowing, but you can see the old post at Group Borrowing: Zero-Cost Memory Safety with Fewer Restrictions. 9 Soon I'll be writing an article on how all of these languages' approaches work and compare with each other, stay tuned! 10 It rejects less code by using first class permissions to merge lifetimes with the type checker. Definitely check it out! 11 Zeta's borrow checker can prove when two references point to different parts of the same data (they're "disjoint"). For example, it can prove list.get_mut(i) and list.get_mut(j) are disjoint when it knows that i != j, even if both indices are runtime values. Read more here! 12 The main differences between Valen and Vale: Valen will have group borrowing, Vale had region borrowing. Valen will have normal generational references, Vale had probabilistic generational references. Valen will have (and Vale didn't have) Rust interop, Rc, comptime. Valen won't have (and Vale did have) perfect replayability, fearless FFI. Both have linear types. Their syntax is still mostly the same, but Valen might change to be more of a "simpler Rust" syntactically. 13 I often called this conflict "the language killer" because it destroyed many versions of many experimental languages, including one of Vale's earlier approaches. Between constraint references and Vale's current blend, there was an approach I called "hybrid-generational