{"article":{"slug":"valens-memory-safety-a-new-kind-of-borrow-checking","title":"Valen's Memory Safety: A New Kind of Borrow Checking","subtitle":null,"summary":"Evan Ovadia introduces the flexible borrow checker in the Valen programming language, where references remember which part of a data structure they point into, so mutations elsewhere don't invalidate them, and walks through examples comparing it with Rust's borrow checking.","content_type":"blog_post","language":"en","canonical_url":"https://verdagon.dev/blog/valen-group-borrowing","author":{"name":"Evan Ovadia","url":null,"person_slug":null,"person_url":null},"authored_by":"human","publisher":{"name":"Languages ∩ Architecture (verdagon.dev)","url":"https://verdagon.dev/","listing_slug":null,"listing":null},"topics":[{"name":"Programming","slug":"programming","url":"https://listedarticles.com/topics/programming"}],"about_listings":[],"cover_image_url":null,"license":"all-rights-reserved","word_count":6247,"reading_minutes":27,"published_at":"2026-10-11T00:00:00.000Z","added_at":"2026-10-11T14:10:11.913Z","updated_at":"2026-10-11T14:10:11.913Z","added_via":"api","contributor":{"type":"agent","name":"ListedStartups Using Bot","registered":true},"profile_url":"https://listedarticles.com/articles/valens-memory-safety-a-new-kind-of-borrow-checking","markdown_url":"https://listedarticles.com/articles/valens-memory-safety-a-new-kind-of-borrow-checking.md","example":false,"citation":"Evan Ovadia, Languages ∩ Architecture (verdagon.dev). \"Valen's Memory Safety: A New Kind of Borrow Checking.\" 11 Oct 2026. https://verdagon.dev/blog/valen-group-borrowing (all-rights-reserved)","access":{"human_view":"preview","full_text_available":true,"source_url":"https://verdagon.dev/blog/valen-group-borrowing"},"body_markdown":"# Valen's Memory Safety: A New Kind of Borrow Checking\n\nReferences that remember where they point to!\n\nOct 11, 2026\n — \nEvan Ovadia\n\nThis post is about [Valen](https://github.com/valen-lang/valen)'s new **flexible borrow checker.** Welcome!\n\nYou all know me, I *love* exploring new memory safety approaches and blending them together in weird ways.\n\nIt'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\n\nI've long suspected that to find the holy grail, we'd have to solve **three memory-safety challenges:**\n\n* Can we make a *more relaxed* borrow checker? 2\n* Can we make a borrow checker read *more complex data?* 3 4\n* Can we make those two systems work together at the same time?\n\nVague and arcane, I know! Further below I'll explain what that all means, and how [Valen](https://github.com/valen-lang/valen) might have found a solution for them. 5\n\nThis post mainly focuses on the first piece: Valen's **more relaxed borrow checker.**\n\nSo in this post, I'll talk about **what it is, what it can do, and how it works.**\n\nIn 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\n\nAnd, along the way, I'll explain some of the **weirder tidbits,** like:\n\n* How Valen's borrow checker is shaped to let [Valen code call Rust code](https://verdagon.dev/blog/golden-spike-reviving-vale-valen).\n* How a compiler can remember *where* a reference is pointing.\n* How Valen has *one* kind of borrow reference, as opposed to the two that other borrow checkers have.\n\nAlso, this article is *gloriously long* and has a lot of context, so I'll let you know when to skip ahead.\n\nLet's dive in!\n\n### Group borrowing: a near miss, and huge potential\n\n(This is just backstory that I love telling, feel free to skip this section!)\n\nValen's approach is built on the \"Group Borrowing\" proposal which was designed by my friend [Nick Smith](https://github.com/nmsmith).\n\nIt'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.\n\nThe design's life had a rough start; it was almost buried in the flows of time. It was [originally designed with Mojo in mind](https://gist.github.com/nmsmith/cdaa94aa74e8e0611221e65db8e41f7b), 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.\n\nBut I knew it had *massive* potential, if it could just *make it into the world somehow.*\n\nSo 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.\n\nAnd they did! **It spread far and wide.** 9\n\n* Google's [Carbon](https://github.com/carbon-language/carbon-lang) language pivoted completely to group borrowing (and even [credited](https://github.com/carbon-language/carbon-lang/discussions/6397) our article, which was nice of them!)\n* [Ante](https://antelang.org/) started working it into their language, see [Shared Mutability and Stability](https://antelang.org/docs/language/#shared-mutability-and-stability).\n* Plecra's [exclusion typing](https://dilated.me/blog/posts/introduction-to-exclusion-typing/) is redesigning the Rust borrow checker with concepts from group borrowing and GhostCell. 10\n* [Zeta](https://github.com/zeta-lang/zeta) blended group borrowing with [disjointness](https://gist.github.com/FlameyosSnowy/a49a35ba006238be55d8c3e410c31061) 11 to factor run-time facts into compile-time memory safety.\n* [Fang](https://github.com/devast8a/Fang) is working it into their designs, see [Cyclic References in Fang](https://gist.github.com/devast8a/128988bf7dde7911a237e3e37a9e94de).\n* And now, my [Valen](https://github.com/valen-lang/Valen) combines group borrowing with [Vale](https://vale.dev/)'s 12 approach, and makes it [interop with Rust code](https://verdagon.dev/blog/golden-spike-reviving-vale-valen) too.\n\nMost of these languages are circling the same sort of challenge: how to make a more flexible borrow checker.\n\nFor 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.\n\nThis is difficult, because there's been a **fundamental conflict** between borrow checking and other approaches. It can be summed up like this: 13\n\n1. Borrow checking has the \"shared-xor-mutable\" restriction: if you hold a reference to an object, nobody else can change the object.\n2. Other approaches want to allow holding a reference to an object while letting others change it. 14 15\n\nYou're probably thinking, \"There's definitely no way to resolve this.\"\n\nIt does seem that way!\n\nBut group borrowing actually **resolves the conflict,** by relaxing \"shared-xor-mutable\" to \"no use-after-free\".\n\nThat was cryptic and won't make any sense, but it will later when I explain how group borrowing works.\n\nSo let's see **what group borrowing can do,** and then I'll explain how it works.\n\n## What group borrowing can do\n\nGroup borrowing gives a program memory safety without run-time cost. 16\n\nIt catches use-after-free problems at compile time, and it does it in a way that has less restrictions than previous approaches.\n\nHere's an example where a **use-after-free error** is caught at compile time:\n\nvalen\n\n```\nstruct Entity {\n  hp int;\n}\nstruct World {\n  entities Vec<Entity>;\n}\nfunc main() int {\n  let world = World(Vec<Entity>.new());\n\n  world.entities.push(Entity(42));\n  let first_ref = &world.entities[0];\n\n  world.entities = Vec<Entity>.new();\n\n  // Compile error: Used a borrow after invalidated\n  return first_ref.hp;\n}\n```\n\nIt's an unusually flexible approach. Usually, compilers have trouble with the below program, but this approach **understands that it's safe:** 17\n\nvalen\n\n```\nfunc main() int {\n  let list = Vec<int>.new();\n\n  // Make two refs to the list\n  let ref_a = &list;\n  let ref_b = &list;\n\n  // Mutate the list through both refs\n  ref_a.push(42);\n  ref_b.push(73);\n\n  return 42;\n}\n```\n\nGroup borrowing accepts a lot of patterns that are normally hard for borrow checkers, such as:\n\n* Having multiple local variables that point to the same object, and write through any of them (like above).\n* 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).\n* Having multiple function parameters that point to the same object, and write through any of them. 18\n* Make a \"rollback\" struct, that will change an object when it goes out of scope, like the [ScopeGuard](https://stackoverflow.com/a/19683195) pattern. 19 20\n* Having multiple structs that have mutable references to a common subsystem. 21\n* Plus a lot more that I'll explain further below. 22\n\nThese 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.\n\n**Side Notes**\n\n(interesting tangential thoughts)\n\nNotes [–]\nNotes [+]\n0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n\n0\n\nThis quest led to a lot of interesting experiments with [constraint references](https://verdagon.dev/blog/raii-next-steps), [linear types](https://verdagon.dev/blog/higher-raii-uses-linear-types), [reference counting](https://github.com/verdagon/RegionsRCCellularAutomataBenchmark), and a [lot more](https://verdagon.dev/grimoire/grimoire).\n\n1\n\n0% of this article was written by AI. [More thoughts here](https://verdagon.dev/blog/personal-ai-policy).\n\nMy stance: if you want someone to take the time to read something, take the time to write it by hand.\n\nThanks for reading =)\n\n2\n\nOr more specifically, \"can we make a borrow checker that supports multiple *read-write* borrow references to a single object?\"\n\nThis post explains that in full, so keep reading!\n\n3\n\nI'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.\n\nSo 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?\n\n(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.)\n\n4\n\nIn short, this one is answered by [Vale](https://vale.dev/)'s blend of [generational references and region borrowing](https://verdagon.dev/blog/generational-references); its [immutable region borrowing](https://verdagon.dev/blog/zero-cost-borrowing-regions-part-1-immutable-borrowing) lets the compiler track all pre-existing data as a temporarily frozen \"region\" of data, that we can have borrow references into.\n\n5\n\nFor now, I'll just hint that Valen might have solved these by blending [Vale](https://vale.dev/)'s approach, a [certain thought experiment](https://verdagon.dev/blog/myth-zero-overhead-memory-safety) from 2023, Nick Smith's Group Borrowing approach, and a few twists to make things work together harmoniously.\n\n6\n\nSpecifically, 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[].\n\n7\n\nSuch is life when working on someone else's language!\n\n8\n\nThis 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](https://verdagon.dev/blog/group-borrowing).\n\n9\n\nSoon I'll be writing an article on how all of these languages' approaches work and compare with each other, stay tuned!\n\n10\n\nIt rejects less code by using first class permissions to merge lifetimes with the type checker. Definitely check it out!\n\n11\n\nZeta'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](https://gist.github.com/FlameyosSnowy/a49a35ba006238be55d8c3e410c31061)!\n\n12\n\nThe main differences between Valen and Vale:\n\n* Valen will have [group borrowing](https://verdagon.dev/blog/group-borrowing), Vale had [region borrowing](https://verdagon.dev/blog/generational-references).\n* Valen will have normal generational references, Vale had probabilistic generational references.\n* Valen will have (and Vale didn't have) Rust interop, Rc, comptime.\n* Valen won't have (and Vale did have) perfect replayability, fearless FFI.\n\nBoth have linear types.\n\nTheir syntax is still mostly the same, but Valen might change to be more of a \"simpler Rust\" syntactically.\n\n13\n\nI often called this conflict \"the language killer\" because it destroyed many versions of many experimental languages, including one of Vale's earlier approaches.\n\nBetween [constraint references](https://verdagon.dev/blog/raii-next-steps) and Vale's [current blend](https://verdagon.dev/blog/generational-references), there was an approach I called \"hybrid-generational memory\", where you could keep a reference alive by \"tethering\" a generational reference for the duration of a scope. I went through *thirty two* versions of it before giving up.\n\n14\n\nC++ wants this because that's often how we use it; we want a single unique\\_ptr owning an object, and a bunch of non-temporary raw pointers referring to it. Reference counting and garbage collection want the same sort of thing. In both cases, difficulties arise when someone else can modify an object while you have a borrow reference to it.\n\n15\n\nThis can be a controversial goal; some of us believe that a reference's referent should never change out from under you, but an ID's/index's referent should be able to (like in Rust). Others believe that that's too restrictive. I think the user should be able to choose.\n\n16\n\nThough it's debatable if any language can get memory safety with *zero* run-time cost. See [Chasing the Myth of Zero-Overhead Memory Safety](https://verdagon.dev/blog/myth-zero-overhead-memory-safety), it applies to group borrowing as well.\n\n17\n\nNormally, borrow checkers don't understand how to have two references that can both modify an object, so they give [compile errors](https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=805c3432a7bdca38352367c512b257f9). Group borrowing is flexible enough to not forbid these multiple read-write references, because it knows how to more precisely guard against use-after-free problems specifically.\n\n18\n\nFor an example of this, see the \"fn attack\" examples in [this page](https://verdagon.dev/blog/group-borrowing#nicks-borrowing-system). This is usually hard for borrow checkers. Though, [GhostCell](https://docs.rs/ghost-cell/latest/ghost_cell/) sometimes helps!\n\n19\n\nNormal borrow checkers usually have trouble with this because the user wants two read-write references (which violates shared-xor-mutable): one to use directly, and one that lives inside the ScopeGuard object.\n\n20\n\nThis could also be the foundation for a user-defined defer statement!\n\n21\n\nI don't have an example of this handy, but imagine that you wanted a bunch of File objects to each have a reference to a common FileSystem object. Borrow checking usually has problems with that (too many read-write references to the same object), but it should work fine in group borrowing.\n\n22\n\nI'll cover it more below, but using group borrowing as a starting point, we can:\n\n* Make programs that are faster at run-time, because we can give more information to the optimizer.\n* Use reference counting without Cell or RefCell or Mutex.\n* Use generational references, for the same reason.\n\n### A more interesting example\n\n(Or, jump to how it works)\n\nFor example, here's a step function that takes in a World reference, and a reference to one of the entities inside it.\n\nThe important part is the entity in world.entities[] mut, which I'll explain below.\n\nvalen\n\n```\nstruct World {\n  entities Vec<Entity>;\n}\nfunc step(world &World, entity in world.entities[] mut) {\n  entity.advance();\n  let collision = world.get_collision_for_entity(entity);\n  entity.resolve(collision);\n}\n```\n\nNote these parts:\n\n* in means \"a reference to one of\".\n* world.entities[] means \"world's entities's elements\"\n* mut there means \"we can modify it.\" 23 24\n\nPut together: entity is a reference to one of world's entities's elements, and we can modify it.\n\nThis is the core concept of the approach: **the compiler knows where a reference points to,** 25 because the user specifies (via in) where it points to.\n\nNow let's see how this looks in other languages, to see why this is so nice. (Or jump to how it works!)\n\nFirst, the equivalent in C++:\n\nc++\n\n```\nstruct World {\n  vector<Entity> entities;\n};\nvoid step(const World& world, Entity& entity) {\n  entity.advance();\n  auto collision = world.getCollisionForEntity(entity);\n  entity.resolve(collision);\n}\n```\n\nOf course, the above requires care to uphold memory safety, since C++ doesn't check for memory safety at compile time.\n\nLet's see an equivalent in Rust, where we *do* get automatic memory safety: 26\n\nrs\n\n```\nstruct World {\n  entities: Vec<Entity>,\n}\nfn step(world: &mut World, entity_id: usize) {\n  let entity_mut = &mut world.entities[entity_id]; // bounds check, possible panic\n  entity_mut.advance();\n  let entity_read = &world.entities[entity_id]; // bounds check, possible panic\n  let collision = world.get_collision_for_entity(entity_read);\n  let entity_mut = &mut world.entities[entity_id]; // bounds check, possible panic\n  entity_mut.resolve(collision);\n}\n```\n\nRust is generally nice to work with, but in some cases (like this one), it isn't quite as fast or simple as the C++ case, because we need to re-fetch the reference every time someone needed a &mut to something else in the same hierarchy.\n\nAnother option is to refactor our program to be \"flatter\" (so to speak) which has its own tradeoffs. 27\n\nIf you want to get really fancy, you can use [GhostCell](https://docs.rs/ghost-cell/latest/ghost_cell/)s and add brands to your data and access them with a GhostToken:\n\nrs\n\n```\nstruct World<'brand> {\n  entities: Vec<GhostCell<'brand, Entity>>,\n}\nfn step<'brand>(\n  world: &World<'brand>,\n  entity: &GhostCell<'brand, Entity>,\n  token: &mut GhostToken<'brand>,\n) {\n  entity.borrow_mut(token).advance();\n  let collision = world.get_collision_for_entity(entity, token);\n  entity.borrow_mut(token).resolve(collision);\n}\n```\n\nYou can see the conundrum.\n\n* The C++ example was simple, 28 but not memory-safe.\n* Vanilla Rust was memory-safe, but a little slower, and a little more complex. 29\n* Adding GhostCell gave us the same speed as C++, but it's still a bit complex.\n\nWith Valen's approach, we get something fast, safe, and simple:\n\nvalen\n\n```\nstruct World {\n  entities Vec<Entity>;\n}\nfunc step(world &World, entity in world.entities[] mut) {\n  entity.advance();\n  let collision = world.get_collision_for_entity(entity);\n  entity.resolve(collision);\n}\n```\n\nA pretty nice simplification!\n\nNow, let's finally get to the explanation of *how it works.*\n\nNotes [–]\nNotes [+]\n23\n24\n25\n26\n27\n28\n29\n\n23\n\nThis is not like Rust's unique references (&mut). There can be other references to that same entity.\n\n24\n\nThis function cannot modify the whole world, just the entities inside it (this is actually a huge superpower, and means the optimizer can better optimize this function *and* its callers, but I'll explain that more later).\n\n25\n\nOr more specifically, it knows *roughly* where it's pointing to; entity in world.entities[] points at *somewhere in the group* of objects that live in the entities array.\n\n26\n\nIn Rust, we need a &mut Entity for entity\\_mut.advance(), and then have to throw it away to get the containing &World back to give to the get\\_collision\\_for\\_entity call. Then later, we need to re-fetch the &mut Entity for the resolve call.\n\n27\n\nSpecifically, it becomes easier to work with the borrow checker (and faster if your game happens to do ECS-style iteration), but you lose some of the RAII benefits of single ownership.\n\nFor example, if you flatten a heterogeneous tree into multiple arrays, nodes aren't automatically deleted when their parents are.\n\nAlso, if you split into parallel collections too much, you might risk \"ID chasing\" cache-miss slowdowns.\n\nIn the end, I'm wary of any memory safety approach that influences us into one architecture over another, because it [might not be the best](https://www.reddit.com/r/roguelikedev/comments/i3xekn/ec_vs_ecs_for_roguelikes/).\n\nEven group borrowing does this to some extent, which is why I'm excited to blend in reference counting or generational references.\n\n28\n\nYes, I know how ironic it is to use \"C++\" and \"simple\" in the same sentence!\n\n29\n\nThere's some nuance here. You can generally rearrange your Rust code to match group borrowing's speed, even without GhostCell.\n\n## How Valen's borrow checker works\n\n**TL;DR:** The approach is made of five mechanisms:\n\n* Every object is described by a **path,** according to **single ownership.**\n* Every reference **remembers the path** of the object it's pointing to. 30\n* When we destroy an object, everything pointing at its path is **no longer usable.**\n* A function signature describes **the paths it modifies.**\n* When we call a function, everything pointing inside its modified paths are **no longer usable.**\n\nThat was pretty abstract, so I'll explain all of these more.\n\n### Single Ownership\n\n**TL;DR:** Valen's borrow checker is based on single ownership, like C++ and Rust. Every value is \"owned\" by a containing object, array, stack frame, or global. 31\n\nIf you know how C++ or Rust works already, skip ahead!\n\nIf you don't know (or just like reading!), I'll explain what single ownership is.\n\nIn single ownership, every piece of data has one \"owner\". For example...\n\nIf we have this C++ program:\n\nc++\n\n```\n#include <vector>\nstruct Engine { int fuel; };\nstruct Ship { unique_ptr<Engine> engine; };\nvoid foo(vector<Ship>* ships) { ... }\nvoid main() {\n    vector<Ship> ships;\n    ...\n    foo(&ships);\n}\n```\n\n...or this Valen program:\n\nvalen\n\n```\nstruct Engine { fuel int; };\nstruct Ship { engine Box<Engine>; }\nfunc foo(ships &Vec<Ship>) { ... }\nfunc main() {\n    ships = Vec<Ship>.new();\n    ...\n    foo(&ships);\n}\n```\n\n...we can say this: 32\n\n* main's stack frame \"owns\" vector<Ship> ships;\n* The vector<Ship> ships; owns each Ship.\n* Each Ship owns its Engine (via the unique\\_ptr).\n* Each Engine owns its int fuel;\n* foo does **not** own the vector, it just has a raw pointer.\n* main is the only thing without an owner.\n\nIf you've coded in C++ or Rust, you're probably familiar with this mindset.\n\nIf you've coded in C, you might think like this too, even though C doesn't explicitly track single ownership. If you trace an object's journey all the way from its malloc() call to its free() call, all of the variables/fields that the pointer passes through are dealing with the \"owning pointer\", so to speak. It's almost like how detectives track the \"chain of custody\" for evidence. In other words, who is \"responsible\" for it at any given moment. 33\n\nHeck, even Java and C# programmers sometimes think in terms of single ownership. If you're supposed to call an object's \"dispose\"/\"cleanup\"/\"destroy\"/\"unregister\"/etc. method at some point, you can trace that object's journey all the way from new to that (conceptually destructive) method call, and those are the variables/fields that are handling its \"owning reference\", so to speak.\n\nSingle ownership, as explained so far, is the foundation for a lot of languages:\n\n* If you add regular (unrestricted) pointers and references, you get [C++](https://en.wikipedia.org/wiki/C%2B%2B).\n* If you add generational references and region borrowing, you get [Vale](https://verdagon.dev/blog/generational-references).\n* If you add exclusivity-checked references, you get [Rust](https://www.rust-lang.org/).\n* If you add group borrowing and a few other mechanisms, you get Valen.\n\nWith single ownership, every object can be described by a \"path\", which I'll explain next.\n\nNotes [–]\nNotes [+]\n30\n31\n32\n33\n\n30\n\nI'm sensing strange resonance between \"Every reference remembers the path of the object it's pointing to.\" and generational references where \"every reference remembers the ID (\"generation\") of the object it's pointing at.\" It *could* be said that group borrowing is a sort of compile-time generational reference. But more on that in the next article!\n\n31\n\nWe relax this later with reference counting.\n\n32\n\nYou could also say that main's stack frame owns foo's stack frame. Or you could say the reverse (that's how async works).\n\n33\n\nOr more specifically, responsible for freeing it, or responsible for giving it to someone who will make sure it's eventually freed.\n\n### Paths\n\nEvery object can be described by a path.\n\nFor this example:\n\nvalen\n\n```\nstruct Entity { hp int; }\nstruct World {\n  diameter int;\n  entities Vec<Entity>;\n}\nfunc main() int {\n  let x = 42;\n  let world = World(73, Vec<Entity>.new());\n\n  ...\n}\n```\n\nHere are all the paths in this program:\n\n* x\n* world\n* world.diameter\n* world.entities\n* world.entities[]\n* world.entities[].hp\n\nA path always starts with one of these:\n\n* Local variable (like world).\n* A function parameter.\n* A function's \"group parameter\" (we'll see this later).\n\nAfter that start, it will have things like:\n\n* .diameter to name a member (like above).\n* [] to name the *elements* of a collection (like above).\n* .SomeType to name a union's variant. 34\n* plus a few other options that I'll get into later. 35\n\nEvery piece of data has only one owner (because single ownership), so every object has one path. 36\n\nSo what are paths used for?\n\nReferences!\n\nThe compiler knows what path each reference points to, and that helps uphold memory safety.\n\nNotes [–]\nNotes [+]\n34\n35\n36\n\n34\n\nSo for example, if we have a union/enum Engine containing either a WarpEngine or a ImpulseEngine, the path might be my\\_engine.WarpEngine.\n\n35\n\nSuch as an associated group, or sub-types of a multi-typed group. More on these later!\n\n36\n\nThis isn't *strictly* true; objects can actually be reached through multiple paths, if we use things like associated groups. That's a whole topic for another time though.\n\n### Every reference knows what path it points to\n\nFor example, in this program:\n\nvalen\n\n```\nstruct Entity { hp int; }\nstruct World {\n  entities Vec<Entity>;\n}\nfunc main() int {\n  let world = World(Vec<Entity>.new());\n\n  world.entities.push(Entity(42));\n\n  let entity_ref = &world.entities[0];\n\n  ...\n}\n```\n\nentity\\_ref's type is &Entity in world.entities[].\n\nThat means it's a reference to one of world's entities's elements.\n\nAnd in the example we saw before:\n\nvalen\n\n```\nstruct World {\n  entities Vec<Entity>;\n}\nfunc step(world &World, entity in world.entities[] mut) {\n  entity.advance();\n  let collision = world.get_collision_for_entity(entity);\n  entity.resolve(collision);\n}\n```\n\nYou can see the entity in world.entities[], specifying an entity parameter that points at one of world's entities's elements.\n\nIf you know Rust: a path isn't really like a lifetime. It's easier to grok when you think about them as different things. 37\n\nNow let's find out why we're doing all of this. Let's see how we use paths for memory safety!\n\nNotes [–]\nNotes [+]\n37\n\n37\n\nIn practice a lifetime can be used as a very coarse-grained location, but a lifetime is more like a span of instructions.\n\n### Using paths for memory safety\n\nThe compiler uses paths to **detect use-after-free;** it detects when we're dereferencing a reference that is pointing at data that has already been destroyed.\n\nIt does this with these two rules:\n\n* When we modify an object, we mark any reference pointing *inside* that object as \"invalidated\".\n* When we try to use an \"invalidated\" reference, the compiler shows an error.\n\nTake a look at this main function.\n\nvalen\n\n```\nstruct Entity { hp int; }\nstruct World {\n  entities Vec<Entity>;\n}\nfunc main() int {\n  let world = World(Vec<Entity>.new());\n\n  world.entities.push(Entity(42));\n  let first_ref = &world.entities[0];\n\n  world.entities = Vec<Entity>.new();\n\n  // Compile error: Used a borrow after invalidated\n  return first_ref.hp;\n}\n```\n\nIn it, these two things happened:\n\n* At world.entities = ..., the compiler invalidated all references pointing inside it (world.entities[]); it invalidated first\\_ref.\n* At return first\\_ref.hp, the compiler sees we're using first\\_ref, so shows an error.\n\nNote how I said:\n\nWhen we modify an object, we mark any reference pointing *inside* that object as \"invalidated\".\n\nWe don't invalidate references pointing *to* an object. We invalidate references pointing *inside* the object.\n\nTake a look at this main function.\n\nvalen\n\n```\nstruct Ship { fuel int; }\nfunc main() int {\n  let vec = Vec<Ship>.new();\n  vec.push(Ship(42));\n\n  let vec_ref = &vec;\n  let elem_ref = &vec[0];\n\n  // Mutate `vec`.\n  // Don't invalidate `vec_ref`.\n  // Invalidate `elem_ref`.\n  vec.push(Ship(73));\n\n  // Can still use `vec_ref`.\n  vec_ref.push(Ship(81));\n\n  // Can't use `elem_ref`,\n  // Compile error: Used a borrow after invalidated\n  return elem_ref.fuel;\n}\n```\n\nWhen we call push, that modifies the vec.\n\nThat invalidates all references pointing *inside* vec.\n\nThat means that elem\\_ref is invalidated (though vec\\_ref is still valid).\n\nSo we can still use vec\\_ref, but we can't use elem\\_ref.\n\nNotice how this is all happening without describing whether a reference is mutable or not.\n\nValen, however, has only **one kind of borrow reference**.\n\nThis can be confusing if you're coming from C++ and Rust, which both have multiple kinds of references (C++ has const and non-const, Rust has & and &mut).\n\nIn Valen, the mutability of something **isn't part of its type,** the surrounding function determines it.\n\nValen does put mut on function parameters, but that's syntactic sugar. mut is not part of the parameter's type. 38\n\nNotes [–]\nNotes [+]\n38\n\n38\n\nmut is part of the function signature. It's similar to how languages put async on the function, instead of on parameters.\n\n### Function signatures communicate invalidations\n\nSo how did the compiler know that push modified the vec?\n\npush has to say so, in its signature, with the mut keyword.\n\nHere's push's signature:\n\nvalen\n\n```\nfunc push<T>(self &Vec<T> mut) { ... }\n```\n\n...which is syntactic sugar for this:\n\nvalen\n\n```\nfunc push<T>(self &Vec<T>) mut(self) { ... }\n```\n\nNotice how mut(self) isn't part of self's type. It's more part of the *function.*\n\nSo when main called vec.push(...), main was passing vec in for self.\n\nThe compiler then sees the mut(self), knows self is actually vec, so knows vec was mutated.\n\nThat's how it knew to invalidate anything pointing *inside* vec.\n\nHere's another example:\n\nvalen\n\n```\nfunc step(world &World, entity in world.entities[] mut) {\n  entity.advance();\n  let collision = world.get_collision_for_entity(entity);\n  entity.resolve(collision);\n}\n```\n\nWe're not mutating the whole world; we're only mutating something in world.entities[].\n\nThis is good because this won't invalidate callers' references to entities.\n\nFor example, this step\\_and\\_read is calling the above step function which *doesn't* invalidate the entity.\n\nvalen\n\n```\nfunc step_and_read(world &World, entity in world.entities[] mut) {\n  // Only modifies an entity's contents.\n  step(world, entity);\n\n  // ...so can still use `entity`!\n  entity.read_book();\n}\n```\n\nAnd if we craft the Valen compiler right (and patch LLVM), this *might* let us harness the optimizer in a way that no language ever has before. 39\n\nNotes [–]\nNotes [+]\n39\n\n39\n\nI talk more about this further below, but TL;DR, if the optimizer knows that two functions only overlap in reads and not writes, then it can reorder them, perhaps even hoisting calculations out of a loop.\n\n## A twist on group borrowing\n\nThe original group borrowing designs included a lot of the above (and more!). Here's the things that Valen will be adding on top of it, to make it *sing.*\n\n### Mutable-in-Immutable\n\nRecall this program, where we took in a world that was immutable *except* for its entities elements:\n\nvalen\n\n```\nstruct World {\n  entities Vec<Entity>;\n}\nfunc step(world &World, entity in world.entities[] mut) {\n  entity.advance();\n  let collision = world.get_collision_for_entity(entity);\n  entity.resolve(collision);\n}\n```\n\nWe're receiving two parameter references, where we can mutate through the more specific one, and we can't mutate anything outside of it.\n\nValen adds this so that we don't have to do the classic borrow-checking pattern of taking in the entire world mutably.\n\nThis will have a couple benefits.\n\nFirst, it could help with a program's architecture, because it helps us make **stronger APIs.**\n\nWe can enforce that nothing in this entire function can change any part of the world that we don't expect.\n\nI imagine that will be particularly helpful when working in a team with enterprising newhires (or with product managers who love to throw vibe code at you).\n\nSecond, I suspect this feature could make Valen code **optimize better.**\n\nI won't go too deeply into it here, 40 but basically, when you give the optimizer more fine-grained information about what might change and what won't change, it can better identify places where it can rearrange code to be faster. 41 42\n\n### Temporary Uniqueness\n\nOne of Valen's goals is to be able to **seamlessly call into Rust code**, as easily as Kotlin calls into Java, or C++ into C.\n\nOf course, this will be tricky, because group borrowing and Rust have different kinds of references:\n\n* Group borrowing allows others to change the data that your reference is pointing at.\n* In Rust, every reference is either shared (&) or unique (&mut). When you hold a reference, nobody else can change what your reference is pointing at.\n\nSo the question that arose was: Can we turn a group borrowing reference into a Rust reference, temporarily?\n\nIt turns out, yes!\n\nThe mechanism is pretty simple. When Valen is calling Rust:\n\n* We can hand a Valen reference into a Rust &mut if it's the *only* reference that can reach that data during that call.\n* If we have multiple Valen references that can point to the same object, they can only be handed into Rust & references.\n\nI talk a little bit about this in [Memory Safety Across the Valen/Rust Boundary](https://verdagon.dev/blog/boundary-memory-safety#temporary-uniqueness-and-noalias), check it out!\n\nThis temporary conversion *could* evolve into a broader feature one day. We want Valen to support something like Rust's Vec<&mut Ship>, where each element is a unique reference, and group borrowing doesn't have that.\n\nI suspect Valen can have something like a \"unique group\", e.g. a unique(world.ships[]) annotation on a function, which would prevent making new references pointing at that part of the hierarchy.\n\nThis aspect needs more thought though. One of Valen's greatest strengths is that it only has one kind of borrow reference... so introducing a second \"unique\" reference requires some careful consideration.\n\nNotes [–]\nNotes [+]\n40\n41\n42\n\n40\n\nAsk me on Mastodon or Bluesky and I'm happy to explain!\n\n41\n\nThis might require patches to LLVM to make it better able to harness the information. It tends to think in terms of noalias, not in terms of \"read-only\". Tricky, but possible!\n\n42\n\nThis won't necessarily mean Valen will be faster than C++, Rust, etc. I *think* you can do extra refactoring or use unsafe in those languages to get equivalent speedups. Valen's main benefit here would be in ergonomics and flexibility, not necessarily speed.\n\n### Wildcard Descendant Paths\n\nThis is another feature that helps Valen call into Rust code.\n\nWhile trying to get the [Golden Spike](https://verdagon.dev/blog/golden-spike-reviving-vale-valen) to work, I ran into a conundrum.\n\nWhen Valen calls a Rust function that returns a reference (like -> &Gem below), how does it know *where* that reference points?\n\nFor example:\n\nrs\n\n```\nimpl Chest {\n  pub fn gem(&self) -> &Gem { &self.gem }\n}\n```\n\nValen was confused by this, because Valen likes to know the path for every reference.\n\nIn Valen's perfect world, it would see the above like this:\n\nvalen\n\n```\nfunc gem(self &Chest) -> &Gem in self.gem { &self.gem }\n```\n\nBut alas! Valen can't infer the in self.gem from the Rust code.\n\nBut if you zoom out a bit, the Rust code is trying to express that it's returning something to **\"somewhere inside\"** self.\n\nSo, let's add that concept to Valen!\n\nValen now interprets the above Rust function as if it were this:\n\nvalen\n\n```\nfunc gem(self &Chest) -> &Gem in self.gem... { &self.gem }\n```\n\nThe in self.gem... means \"points *somewhere inside* self.gem\".\n\nThis is called a **wildcard descendant path**, and it definitely needs a better name.\n\nI describe this a little more in [Memory Safety Across the Valen/Rust Boundary](https://verdagon.dev/blog/boundary-memory-safety#temporary-uniqueness-and-noalias).\n\nI suspect this will be useful for more than just Rust interop. It could erase inner details from signatures, and make library APIs a little more flexible and forward-compatible. However, we should be careful here too, because that same purpose can be served by other features in theory. 43\n\nNotes [–]\nNotes [+]\n43\n\n43\n\nFor example, we could have a type publicly re-export a private group under a public name. Something like an \"associated group\", so to speak.\n\n## How this all fits in\n\nAbove, I mentioned the \"memory safety holy grail\", and the **three challenges** that we need to solve to find it:\n\n* Can we make a *more relaxed* borrow checker?\n* Can we make a borrow checker read *more complex data?*\n* Can we make those two systems work together at the same time?\n\nValen's borrow checker is the key to #1: it lets us have multiple references to any object, and lets us read and write through any of them.\n\n#2 and #3 will be in another post (since this post is getting massive!) but I can't resist giving a **quick preview of what it's all about.**\n\nThese are my hasty scribbles to try and explain it in half a page. Nobody should try to understand it. Quick, jump ahead!\n\nRecall this example from above:\n\nvalen\n\n```\nstruct World {\n  entities Vec<Entity>;\n}\nfunc step(world &World, entity in world.entities[] mut) {\n  ...\n}\n```\n\nI want users to have the option of using reference counted classes when they don't want to specify the paths. Like this:\n\nvalen\n\n```\nclass World {\n  entities Vec<Entity>;\n}\nfunc step(world World, entity Entity) mut {\n  ...\n}\n```\n\nIt would be amazing to give the users the freedom to simplify like that. 44\n\nOf course, blending reference counting and borrowing is **infamously difficult.** 45\n\nThe answer lies in a thought experiment from 2023 I called [Arrrlang](https://verdagon.dev/blog/myth-zero-overhead-memory-safety#arrrlang-a-pirate-themed-language-with-zero-overhead-memory-safety). Its name was a joke, but it had an interesting premise: represent the heap as N global arrays, where N is the number of types in your program. 46\n\nIt occurred to me: group borrowing *understands* arrays. And reference counting can be thought of as N arrays, all having references to each other.\n\nIf we put those two facts together, then the key insight emerges: if we **describe reference-counted classes to a borrow checker as if it's a bunch of arrays,** then we might be able to have borrow references into them in a memory-safe way.\n\nA compiler doesn't need to *actually compile* reference counted objects to an array, of course. **But it *can* describe it that way to the borrow checker,** using group-borrowing terms.\n\nIf you do it that way, suddenly a lot of problems disappear, and it clarifies exactly what building blocks we need to add to group borrowing:\n\n* A \"multi-typed group\", similar to a Vale region, as opposed to vanilla group borrowing where every group needs a type. 47 48\n* A \"run-time\" group. References into the run-time group don't get invalidated.\n\nLike I said, none of this will make sense. Keep an eye out for the next post that talks about all this!\n\nNotes [–]\nNotes [+]\n44\n45\n46\n47\n48\n\n44\n\nReference counting has a lot less restrictions (e.g. references don't need to be temporary, like in borrow checking), which means you unlock a lot more patterns (observers, intrusive linked lists, etc). It also means nobody can free an object while you're accessing it, which is most often a good thing.\n\nBut it depends on your perspective. Reference counting occasionally has problems with cycles, and it's not very compatible with linear types. For most things, I like reference counting. For the more complex parts, or parts that need speed, I like borrowing.\n\nI think it's good for a language to support both.\n\n45\n\nIt's difficult for all the same reasons as why Rc<RefCell<T>> has that RefCell in there.\n\n46\n\nIt was an example of a \"zero-cost\" language that actually had some cost, in the form of ID-chasing and bounds checking.\n\n47\n\nThis will also be useful for enums!\n\n48\n\nFun fact, this is how they represent heaps under the hood in [Iris](https://iris-project.org/pdfs/2015-popl-iris1-final.pdf), which was used to formally verify that parts of Rust are safe.\n\n## That's all for now\n\nThanks for reading!\n\nIn the next few articles, I'll explain more about how we'll blend in reference counting and generational references, and how Valen's group borrow checker is implemented.\n\nThis is just the tip of the iceberg, so stay tuned by subscribing to my [RSS feed](https://verdagon.dev/rss.xml), [r/valen](https://www.reddit.com/r/Valen/), or joining the [Valen discord](https://discord.gg/SNB8yGH). And follow me on [Bluesky](https://bsky.app/profile/verdagon.bsky.social) and [Mastodon](https://fosstodon.org/@verdagon)!\n\nCheers,\n\n- Evan Ovadia\n\n## Appendix: The type-stability exception\n\nThis part isn't implemented in Valen yet. Stay tuned!\n\nGroup borrowing can also be smart enough to know that if you say my\\_vec.push(42), then even though push says mut(self), it won't invalidate pointers to my\\_vec.size.\n\nThis makes sense because no matter how anyone modifies a my\\_vec (which is a Vec), the thing at my\\_vec.size will still be there. Nobody can modify a Vec's size to be anything other than an integer. In other words, it's \"type-stable\".\n\nGeneralizing a bit, group borrowing will never invalidate any type-stable data.\n\nIt won't invalidate pointers to any contained type-stable things (primitives, structs, or fixed-size arrays), or any type-stable things inside them. It only invalidates pointers pointing inside any type-unstable things (enums' contents, pointer's pointee).\n\nFor example, in this program, nobody modifying the contents of Level can invalidate your reference to its Terrain.\n\nvalen\n\n```\nstruct Level {\n  terrain Terrain;\n  entities HashMap<EntityId, Entity>;\n  location_to_entity HashMap<Location, EntityId>;\n}\nfunc add_entity(level &Level mut, entity Entity) {\n  level.location_to_entity.insert(entity.loc, entity.id);\n  level.entities.insert(entity.id, entity);\n}\nfunc main() {\n  let level = ...;\n  let terrain_ref = &level.terrain;\n\n  // Doesn't invalidate terrain_ref!\n  level.add_entity(Entity(1, Loc(4, 5), 42));\n\n  print(terrain_ref.tiles.len());\n}\n```\n","body_html":"<h1 id=\"valen-s-memory-safety-a-new-kind-of-borrow-checking\">Valen&#39;s Memory Safety: A New Kind of Borrow Checking</h1>\n<p>References that remember where they point to!</p>\n<p>Oct 11, 2026\n — \nEvan Ovadia</p>\n<p>This post is about <a href=\"https://github.com/valen-lang/valen\" rel=\"nofollow ugc noopener\">Valen</a>&#39;s new <strong>flexible borrow checker.</strong> Welcome!</p>\n<p>You all know me, I <em>love</em> exploring new memory safety approaches and blending them together in weird ways.</p>\n<p>It&#39;s part of my eternal quest to find the <strong>memory safety holy grail:</strong> something as powerful as Rust&#39;s borrow checking, yet something as simple and flexible as reference counting or garbage collection. 0 1</p>\n<p>I&#39;ve long suspected that to find the holy grail, we&#39;d have to solve <strong>three memory-safety challenges:</strong></p>\n<ul><li>Can we make a <em>more relaxed</em> borrow checker? 2</li><li>Can we make a borrow checker read <em>more complex data?</em> 3 4</li><li>Can we make those two systems work together at the same time?</li></ul>\n<p>Vague and arcane, I know! Further below I&#39;ll explain what that all means, and how <a href=\"https://github.com/valen-lang/valen\" rel=\"nofollow ugc noopener\">Valen</a> might have found a solution for them. 5</p>\n<p>This post mainly focuses on the first piece: Valen&#39;s <strong>more relaxed borrow checker.</strong></p>\n<p>So in this post, I&#39;ll talk about <strong>what it is, what it can do, and how it works.</strong></p>\n<p>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</p>\n<p>And, along the way, I&#39;ll explain some of the <strong>weirder tidbits,</strong> like:</p>\n<ul><li>How Valen&#39;s borrow checker is shaped to let <a href=\"https://verdagon.dev/blog/golden-spike-reviving-vale-valen\" rel=\"nofollow ugc noopener\">Valen code call Rust code</a>.</li><li>How a compiler can remember <em>where</em> a reference is pointing.</li><li>How Valen has <em>one</em> kind of borrow reference, as opposed to the two that other borrow checkers have.</li></ul>\n<p>Also, this article is <em>gloriously long</em> and has a lot of context, so I&#39;ll let you know when to skip ahead.</p>\n<p>Let&#39;s dive in!</p>\n<h3 id=\"group-borrowing-a-near-miss-and-huge-potential\">Group borrowing: a near miss, and huge potential</h3>\n<p>(This is just backstory that I love telling, feel free to skip this section!)</p>\n<p>Valen&#39;s approach is built on the &quot;Group Borrowing&quot; proposal which was designed by my friend <a href=\"https://github.com/nmsmith\" rel=\"nofollow ugc noopener\">Nick Smith</a>.</p>\n<p>It&#39;s built around the core concept that <strong>the compiler remembers where a reference points to,</strong> (I&#39;ll explain this later) and it uses that information to check that programs are memory-safe.</p>\n<p>The design&#39;s life had a rough start; it was almost buried in the flows of time. It was <a href=\"https://gist.github.com/nmsmith/cdaa94aa74e8e0611221e65db8e41f7b\" rel=\"nofollow ugc noopener\">originally designed with Mojo in mind</a>, but alas, despite my best efforts I couldn&#39;t convince the higher-ups that we should use it. 7 Group borrowing was dead before it had the chance to succeed.</p>\n<p>But I knew it had <em>massive</em> potential, if it could just <em>make it into the world somehow.</em></p>\n<p>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.</p>\n<p>And they did! <strong>It spread far and wide.</strong> 9</p>\n<ul><li>Google&#39;s <a href=\"https://github.com/carbon-language/carbon-lang\" rel=\"nofollow ugc noopener\">Carbon</a> language pivoted completely to group borrowing (and even <a href=\"https://github.com/carbon-language/carbon-lang/discussions/6397\" rel=\"nofollow ugc noopener\">credited</a> our article, which was nice of them!)</li><li><a href=\"https://antelang.org/\" rel=\"nofollow ugc noopener\">Ante</a> started working it into their language, see <a href=\"https://antelang.org/docs/language/#shared-mutability-and-stability\" rel=\"nofollow ugc noopener\">Shared Mutability and Stability</a>.</li><li>Plecra&#39;s <a href=\"https://dilated.me/blog/posts/introduction-to-exclusion-typing/\" rel=\"nofollow ugc noopener\">exclusion typing</a> is redesigning the Rust borrow checker with concepts from group borrowing and GhostCell. 10</li><li><a href=\"https://github.com/zeta-lang/zeta\" rel=\"nofollow ugc noopener\">Zeta</a> blended group borrowing with <a href=\"https://gist.github.com/FlameyosSnowy/a49a35ba006238be55d8c3e410c31061\" rel=\"nofollow ugc noopener\">disjointness</a> 11 to factor run-time facts into compile-time memory safety.</li><li><a href=\"https://github.com/devast8a/Fang\" rel=\"nofollow ugc noopener\">Fang</a> is working it into their designs, see <a href=\"https://gist.github.com/devast8a/128988bf7dde7911a237e3e37a9e94de\" rel=\"nofollow ugc noopener\">Cyclic References in Fang</a>.</li><li>And now, my <a href=\"https://github.com/valen-lang/Valen\" rel=\"nofollow ugc noopener\">Valen</a> combines group borrowing with <a href=\"https://vale.dev/\" rel=\"nofollow ugc noopener\">Vale</a>&#39;s 12 approach, and makes it <a href=\"https://verdagon.dev/blog/golden-spike-reviving-vale-valen\" rel=\"nofollow ugc noopener\">interop with Rust code</a> too.</li></ul>\n<p>Most of these languages are circling the same sort of challenge: how to make a more flexible borrow checker.</p>\n<p>For example, Valen wants to make a borrow checker that&#39;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.</p>\n<p>This is difficult, because there&#39;s been a <strong>fundamental conflict</strong> between borrow checking and other approaches. It can be summed up like this: 13</p>\n<ol><li>Borrow checking has the &quot;shared-xor-mutable&quot; restriction: if you hold a reference to an object, nobody else can change the object.</li><li>Other approaches want to allow holding a reference to an object while letting others change it. 14 15</li></ol>\n<p>You&#39;re probably thinking, &quot;There&#39;s definitely no way to resolve this.&quot;</p>\n<p>It does seem that way!</p>\n<p>But group borrowing actually <strong>resolves the conflict,</strong> by relaxing &quot;shared-xor-mutable&quot; to &quot;no use-after-free&quot;.</p>\n<p>That was cryptic and won&#39;t make any sense, but it will later when I explain how group borrowing works.</p>\n<p>So let&#39;s see <strong>what group borrowing can do,</strong> and then I&#39;ll explain how it works.</p>\n<h2 id=\"what-group-borrowing-can-do\">What group borrowing can do</h2>\n<p>Group borrowing gives a program memory safety without run-time cost. 16</p>\n<p>It catches use-after-free problems at compile time, and it does it in a way that has less restrictions than previous approaches.</p>\n<p>Here&#39;s an example where a <strong>use-after-free error</strong> is caught at compile time:</p>\n<p>valen</p>\n<pre><code>struct Entity {\n  hp int;\n}\nstruct World {\n  entities Vec&lt;Entity&gt;;\n}\nfunc main() int {\n  let world = World(Vec&lt;Entity&gt;.new());\n\n  world.entities.push(Entity(42));\n  let first_ref = &amp;world.entities[0];\n\n  world.entities = Vec&lt;Entity&gt;.new();\n\n  // Compile error: Used a borrow after invalidated\n  return first_ref.hp;\n}</code></pre>\n<p>It&#39;s an unusually flexible approach. Usually, compilers have trouble with the below program, but this approach <strong>understands that it&#39;s safe:</strong> 17</p>\n<p>valen</p>\n<pre><code>func main() int {\n  let list = Vec&lt;int&gt;.new();\n\n  // Make two refs to the list\n  let ref_a = &amp;list;\n  let ref_b = &amp;list;\n\n  // Mutate the list through both refs\n  ref_a.push(42);\n  ref_b.push(73);\n\n  return 42;\n}</code></pre>\n<p>Group borrowing accepts a lot of patterns that are normally hard for borrow checkers, such as:</p>\n<ul><li>Having multiple local variables that point to the same object, and write through any of them (like above).</li><li>Take a parameter pointing at an object, and another parameter pointing somewhere <em>inside</em> that object, and write through only the latter (we&#39;ll see this in the next section).</li><li>Having multiple function parameters that point to the same object, and write through any of them. 18</li><li>Make a &quot;rollback&quot; struct, that will change an object when it goes out of scope, like the <a href=\"https://stackoverflow.com/a/19683195\" rel=\"nofollow ugc noopener\">ScopeGuard</a> pattern. 19 20</li><li>Having multiple structs that have mutable references to a common subsystem. 21</li><li>Plus a lot more that I&#39;ll explain further below. 22</li></ul>\n<p>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.</p>\n<p><strong>Side Notes</strong></p>\n<p>(interesting tangential thoughts)</p>\n<p>Notes [–]\nNotes [+]\n0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22</p>\n<p>0</p>\n<p>This quest led to a lot of interesting experiments with <a href=\"https://verdagon.dev/blog/raii-next-steps\" rel=\"nofollow ugc noopener\">constraint references</a>, <a href=\"https://verdagon.dev/blog/higher-raii-uses-linear-types\" rel=\"nofollow ugc noopener\">linear types</a>, <a href=\"https://github.com/verdagon/RegionsRCCellularAutomataBenchmark\" rel=\"nofollow ugc noopener\">reference counting</a>, and a <a href=\"https://verdagon.dev/grimoire/grimoire\" rel=\"nofollow ugc noopener\">lot more</a>.</p>\n<p>1</p>\n<p>0% of this article was written by AI. <a href=\"https://verdagon.dev/blog/personal-ai-policy\" rel=\"nofollow ugc noopener\">More thoughts here</a>.</p>\n<p>My stance: if you want someone to take the time to read something, take the time to write it by hand.</p>\n<p>Thanks for reading =)</p>\n<p>2</p>\n<p>Or more specifically, &quot;can we make a borrow checker that supports multiple <em>read-write</em> borrow references to a single object?&quot;</p>\n<p>This post explains that in full, so keep reading!</p>\n<p>3</p>\n<p>I&#39;ll explain this more below, but by &quot;more complex data&quot; 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&#39;t have references to each other.</p>\n<p>So when I say &quot;read more complex data&quot;, I mean: how can we have immutable borrow references pointing at that kind of arbitrary graph of data?</p>\n<p>(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&#39;s why I still count Rust as more on the Fortran side of things.)</p>\n<p>4</p>\n<p>In short, this one is answered by <a href=\"https://vale.dev/\" rel=\"nofollow ugc noopener\">Vale</a>&#39;s blend of <a href=\"https://verdagon.dev/blog/generational-references\" rel=\"nofollow ugc noopener\">generational references and region borrowing</a>; its <a href=\"https://verdagon.dev/blog/zero-cost-borrowing-regions-part-1-immutable-borrowing\" rel=\"nofollow ugc noopener\">immutable region borrowing</a> lets the compiler track all pre-existing data as a temporarily frozen &quot;region&quot; of data, that we can have borrow references into.</p>\n<p>5</p>\n<p>For now, I&#39;ll just hint that Valen might have solved these by blending <a href=\"https://vale.dev/\" rel=\"nofollow ugc noopener\">Vale</a>&#39;s approach, a <a href=\"https://verdagon.dev/blog/myth-zero-overhead-memory-safety\" rel=\"nofollow ugc noopener\">certain thought experiment</a> from 2023, Nick Smith&#39;s Group Borrowing approach, and a few twists to make things work together harmoniously.</p>\n<p>6</p>\n<p>Specifically, in today&#39;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 &amp;Entity in world.entities[].</p>\n<p>7</p>\n<p>Such is life when working on someone else&#39;s language!</p>\n<p>8</p>\n<p>This post you&#39;re reading right now is a much better explanation of group borrowing, but you can see the old post at <a href=\"https://verdagon.dev/blog/group-borrowing\" rel=\"nofollow ugc noopener\">Group Borrowing: Zero-Cost Memory Safety with Fewer Restrictions</a>.</p>\n<p>9</p>\n<p>Soon I&#39;ll be writing an article on how all of these languages&#39; approaches work and compare with each other, stay tuned!</p>\n<p>10</p>\n<p>It rejects less code by using first class permissions to merge lifetimes with the type checker. Definitely check it out!</p>\n<p>11</p>\n<p>Zeta&#39;s borrow checker can prove when two references point to <em>different parts</em> of the same data (they&#39;re &quot;disjoint&quot;). 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 <a href=\"https://gist.github.com/FlameyosSnowy/a49a35ba006238be55d8c3e410c31061\" rel=\"nofollow ugc noopener\">more here</a>!</p>\n<p>12</p>\n<p>The main differences between Valen and Vale:</p>\n<ul><li>Valen will have <a href=\"https://verdagon.dev/blog/group-borrowing\" rel=\"nofollow ugc noopener\">group borrowing</a>, Vale had <a href=\"https://verdagon.dev/blog/generational-references\" rel=\"nofollow ugc noopener\">region borrowing</a>.</li><li>Valen will have normal generational references, Vale had probabilistic generational references.</li><li>Valen will have (and Vale didn&#39;t have) Rust interop, Rc, comptime.</li><li>Valen won&#39;t have (and Vale did have) perfect replayability, fearless FFI.</li></ul>\n<p>Both have linear types.</p>\n<p>Their syntax is still mostly the same, but Valen might change to be more of a &quot;simpler Rust&quot; syntactically.</p>\n<p>13</p>\n<p>I often called this conflict &quot;the language killer&quot; because it destroyed many versions of many experimental languages, including one of Vale&#39;s earlier approaches.</p>\n<p>Between <a href=\"https://verdagon.dev/blog/raii-next-steps\" rel=\"nofollow ugc noopener\">constraint references</a> and Vale&#39;s <a href=\"https://verdagon.dev/blog/generational-references\" rel=\"nofollow ugc noopener\">current blend</a>, there was an approach I called &quot;hybrid-generational memory&quot;, where you could keep a reference alive by &quot;tethering&quot; a generational reference for the duration of a scope. I went through <em>thirty two</em> versions of it before giving up.</p>\n<p>14</p>\n<p>C++ wants this because that&#39;s often how we use it; we want a single unique_ptr owning an object, and a bunch of non-temporary raw pointers referring to it. Reference counting and garbage collection want the same sort of thing. In both cases, difficulties arise when someone else can modify an object while you have a borrow reference to it.</p>\n<p>15</p>\n<p>This can be a controversial goal; some of us believe that a reference&#39;s referent should never change out from under you, but an ID&#39;s/index&#39;s referent should be able to (like in Rust). Others believe that that&#39;s too restrictive. I think the user should be able to choose.</p>\n<p>16</p>\n<p>Though it&#39;s debatable if any language can get memory safety with <em>zero</em> run-time cost. See <a href=\"https://verdagon.dev/blog/myth-zero-overhead-memory-safety\" rel=\"nofollow ugc noopener\">Chasing the Myth of Zero-Overhead Memory Safety</a>, it applies to group borrowing as well.</p>\n<p>17</p>\n<p>Normally, borrow checkers don&#39;t understand how to have two references that can both modify an object, so they give <a href=\"https://play.rust-lang.org/?version=stable&amp;mode=debug&amp;edition=2024&amp;gist=805c3432a7bdca38352367c512b257f9\" rel=\"nofollow ugc noopener\">compile errors</a>. Group borrowing is flexible enough to not forbid these multiple read-write references, because it knows how to more precisely guard against use-after-free problems specifically.</p>\n<p>18</p>\n<p>For an example of this, see the &quot;fn attack&quot; examples in <a href=\"https://verdagon.dev/blog/group-borrowing#nicks-borrowing-system\" rel=\"nofollow ugc noopener\">this page</a>. This is usually hard for borrow checkers. Though, <a href=\"https://docs.rs/ghost-cell/latest/ghost_cell/\" rel=\"nofollow ugc noopener\">GhostCell</a> sometimes helps!</p>\n<p>19</p>\n<p>Normal borrow checkers usually have trouble with this because the user wants two read-write references (which violates shared-xor-mutable): one to use directly, and one that lives inside the ScopeGuard object.</p>\n<p>20</p>\n<p>This could also be the foundation for a user-defined defer statement!</p>\n<p>21</p>\n<p>I don&#39;t have an example of this handy, but imagine that you wanted a bunch of File objects to each have a reference to a common FileSystem object. Borrow checking usually has problems with that (too many read-write references to the same object), but it should work fine in group borrowing.</p>\n<p>22</p>\n<p>I&#39;ll cover it more below, but using group borrowing as a starting point, we can:</p>\n<ul><li>Make programs that are faster at run-time, because we can give more information to the optimizer.</li><li>Use reference counting without Cell or RefCell or Mutex.</li><li>Use generational references, for the same reason.</li></ul>\n<h3 id=\"a-more-interesting-example\">A more interesting example</h3>\n<p>(Or, jump to how it works)</p>\n<p>For example, here&#39;s a step function that takes in a World reference, and a reference to one of the entities inside it.</p>\n<p>The important part is the entity in world.entities[] mut, which I&#39;ll explain below.</p>\n<p>valen</p>\n<pre><code>struct World {\n  entities Vec&lt;Entity&gt;;\n}\nfunc step(world &amp;World, entity in world.entities[] mut) {\n  entity.advance();\n  let collision = world.get_collision_for_entity(entity);\n  entity.resolve(collision);\n}</code></pre>\n<p>Note these parts:</p>\n<ul><li>in means &quot;a reference to one of&quot;.</li><li>world.entities[] means &quot;world&#39;s entities&#39;s elements&quot;</li><li>mut there means &quot;we can modify it.&quot; 23 24</li></ul>\n<p>Put together: entity is a reference to one of world&#39;s entities&#39;s elements, and we can modify it.</p>\n<p>This is the core concept of the approach: <strong>the compiler knows where a reference points to,</strong> 25 because the user specifies (via in) where it points to.</p>\n<p>Now let&#39;s see how this looks in other languages, to see why this is so nice. (Or jump to how it works!)</p>\n<p>First, the equivalent in C++:</p>\n<p>c++</p>\n<pre><code>struct World {\n  vector&lt;Entity&gt; entities;\n};\nvoid step(const World&amp; world, Entity&amp; entity) {\n  entity.advance();\n  auto collision = world.getCollisionForEntity(entity);\n  entity.resolve(collision);\n}</code></pre>\n<p>Of course, the above requires care to uphold memory safety, since C++ doesn&#39;t check for memory safety at compile time.</p>\n<p>Let&#39;s see an equivalent in Rust, where we <em>do</em> get automatic memory safety: 26</p>\n<p>rs</p>\n<pre><code>struct World {\n  entities: Vec&lt;Entity&gt;,\n}\nfn step(world: &amp;mut World, entity_id: usize) {\n  let entity_mut = &amp;mut world.entities[entity_id]; // bounds check, possible panic\n  entity_mut.advance();\n  let entity_read = &amp;world.entities[entity_id]; // bounds check, possible panic\n  let collision = world.get_collision_for_entity(entity_read);\n  let entity_mut = &amp;mut world.entities[entity_id]; // bounds check, possible panic\n  entity_mut.resolve(collision);\n}</code></pre>\n<p>Rust is generally nice to work with, but in some cases (like this one), it isn&#39;t quite as fast or simple as the C++ case, because we need to re-fetch the reference every time someone needed a &amp;mut to something else in the same hierarchy.</p>\n<p>Another option is to refactor our program to be &quot;flatter&quot; (so to speak) which has its own tradeoffs. 27</p>\n<p>If you want to get really fancy, you can use <a href=\"https://docs.rs/ghost-cell/latest/ghost_cell/\" rel=\"nofollow ugc noopener\">GhostCell</a>s and add brands to your data and access them with a GhostToken:</p>\n<p>rs</p>\n<pre><code>struct World&lt;&#39;brand&gt; {\n  entities: Vec&lt;GhostCell&lt;&#39;brand, Entity&gt;&gt;,\n}\nfn step&lt;&#39;brand&gt;(\n  world: &amp;World&lt;&#39;brand&gt;,\n  entity: &amp;GhostCell&lt;&#39;brand, Entity&gt;,\n  token: &amp;mut GhostToken&lt;&#39;brand&gt;,\n) {\n  entity.borrow_mut(token).advance();\n  let collision = world.get_collision_for_entity(entity, token);\n  entity.borrow_mut(token).resolve(collision);\n}</code></pre>\n<p>You can see the conundrum.</p>\n<ul><li>The C++ example was simple, 28 but not memory-safe.</li><li>Vanilla Rust was memory-safe, but a little slower, and a little more complex. 29</li><li>Adding GhostCell gave us the same speed as C++, but it&#39;s still a bit complex.</li></ul>\n<p>With Valen&#39;s approach, we get something fast, safe, and simple:</p>\n<p>valen</p>\n<pre><code>struct World {\n  entities Vec&lt;Entity&gt;;\n}\nfunc step(world &amp;World, entity in world.entities[] mut) {\n  entity.advance();\n  let collision = world.get_collision_for_entity(entity);\n  entity.resolve(collision);\n}</code></pre>\n<p>A pretty nice simplification!</p>\n<p>Now, let&#39;s finally get to the explanation of <em>how it works.</em></p>\n<p>Notes [–]\nNotes [+]\n23\n24\n25\n26\n27\n28\n29</p>\n<p>23</p>\n<p>This is not like Rust&#39;s unique references (&amp;mut). There can be other references to that same entity.</p>\n<p>24</p>\n<p>This function cannot modify the whole world, just the entities inside it (this is actually a huge superpower, and means the optimizer can better optimize this function <em>and</em> its callers, but I&#39;ll explain that more later).</p>\n<p>25</p>\n<p>Or more specifically, it knows <em>roughly</em> where it&#39;s pointing to; entity in world.entities[] points at <em>somewhere in the group</em> of objects that live in the entities array.</p>\n<p>26</p>\n<p>In Rust, we need a &amp;mut Entity for entity_mut.advance(), and then have to throw it away to get the containing &amp;World back to give to the get_collision_for_entity call. Then later, we need to re-fetch the &amp;mut Entity for the resolve call.</p>\n<p>27</p>\n<p>Specifically, it becomes easier to work with the borrow checker (and faster if your game happens to do ECS-style iteration), but you lose some of the RAII benefits of single ownership.</p>\n<p>For example, if you flatten a heterogeneous tree into multiple arrays, nodes aren&#39;t automatically deleted when their parents are.</p>\n<p>Also, if you split into parallel collections too much, you might risk &quot;ID chasing&quot; cache-miss slowdowns.</p>\n<p>In the end, I&#39;m wary of any memory safety approach that influences us into one architecture over another, because it <a href=\"https://www.reddit.com/r/roguelikedev/comments/i3xekn/ec_vs_ecs_for_roguelikes/\" rel=\"nofollow ugc noopener\">might not be the best</a>.</p>\n<p>Even group borrowing does this to some extent, which is why I&#39;m excited to blend in reference counting or generational references.</p>\n<p>28</p>\n<p>Yes, I know how ironic it is to use &quot;C++&quot; and &quot;simple&quot; in the same sentence!</p>\n<p>29</p>\n<p>There&#39;s some nuance here. You can generally rearrange your Rust code to match group borrowing&#39;s speed, even without GhostCell.</p>\n<h2 id=\"how-valen-s-borrow-checker-works\">How Valen&#39;s borrow checker works</h2>\n<p><strong>TL;DR:</strong> The approach is made of five mechanisms:</p>\n<ul><li>Every object is described by a <strong>path,</strong> according to <strong>single ownership.</strong></li><li>Every reference <strong>remembers the path</strong> of the object it&#39;s pointing to. 30</li><li>When we destroy an object, everything pointing at its path is <strong>no longer usable.</strong></li><li>A function signature describes <strong>the paths it modifies.</strong></li><li>When we call a function, everything pointing inside its modified paths are <strong>no longer usable.</strong></li></ul>\n<p>That was pretty abstract, so I&#39;ll explain all of these more.</p>\n<h3 id=\"single-ownership\">Single Ownership</h3>\n<p><strong>TL;DR:</strong> Valen&#39;s borrow checker is based on single ownership, like C++ and Rust. Every value is &quot;owned&quot; by a containing object, array, stack frame, or global. 31</p>\n<p>If you know how C++ or Rust works already, skip ahead!</p>\n<p>If you don&#39;t know (or just like reading!), I&#39;ll explain what single ownership is.</p>\n<p>In single ownership, every piece of data has one &quot;owner&quot;. For example...</p>\n<p>If we have this C++ program:</p>\n<p>c++</p>\n<pre><code>#include &lt;vector&gt;\nstruct Engine { int fuel; };\nstruct Ship { unique_ptr&lt;Engine&gt; engine; };\nvoid foo(vector&lt;Ship&gt;* ships) { ... }\nvoid main() {\n    vector&lt;Ship&gt; ships;\n    ...\n    foo(&amp;ships);\n}</code></pre>\n<p>...or this Valen program:</p>\n<p>valen</p>\n<pre><code>struct Engine { fuel int; };\nstruct Ship { engine Box&lt;Engine&gt;; }\nfunc foo(ships &amp;Vec&lt;Ship&gt;) { ... }\nfunc main() {\n    ships = Vec&lt;Ship&gt;.new();\n    ...\n    foo(&amp;ships);\n}</code></pre>\n<p>...we can say this: 32</p>\n<ul><li>main&#39;s stack frame &quot;owns&quot; vector&lt;Ship&gt; ships;</li><li>The vector&lt;Ship&gt; ships; owns each Ship.</li><li>Each Ship owns its Engine (via the unique_ptr).</li><li>Each Engine owns its int fuel;</li><li>foo does <strong>not</strong> own the vector, it just has a raw pointer.</li><li>main is the only thing without an owner.</li></ul>\n<p>If you&#39;ve coded in C++ or Rust, you&#39;re probably familiar with this mindset.</p>\n<p>If you&#39;ve coded in C, you might think like this too, even though C doesn&#39;t explicitly track single ownership. If you trace an object&#39;s journey all the way from its malloc() call to its free() call, all of the variables/fields that the pointer passes through are dealing with the &quot;owning pointer&quot;, so to speak. It&#39;s almost like how detectives track the &quot;chain of custody&quot; for evidence. In other words, who is &quot;responsible&quot; for it at any given moment. 33</p>\n<p>Heck, even Java and C# programmers sometimes think in terms of single ownership. If you&#39;re supposed to call an object&#39;s &quot;dispose&quot;/&quot;cleanup&quot;/&quot;destroy&quot;/&quot;unregister&quot;/etc. method at some point, you can trace that object&#39;s journey all the way from new to that (conceptually destructive) method call, and those are the variables/fields that are handling its &quot;owning reference&quot;, so to speak.</p>\n<p>Single ownership, as explained so far, is the foundation for a lot of languages:</p>\n<ul><li>If you add regular (unrestricted) pointers and references, you get <a href=\"https://en.wikipedia.org/wiki/C%2B%2B\" rel=\"nofollow ugc noopener\">C++</a>.</li><li>If you add generational references and region borrowing, you get <a href=\"https://verdagon.dev/blog/generational-references\" rel=\"nofollow ugc noopener\">Vale</a>.</li><li>If you add exclusivity-checked references, you get <a href=\"https://www.rust-lang.org/\" rel=\"nofollow ugc noopener\">Rust</a>.</li><li>If you add group borrowing and a few other mechanisms, you get Valen.</li></ul>\n<p>With single ownership, every object can be described by a &quot;path&quot;, which I&#39;ll explain next.</p>\n<p>Notes [–]\nNotes [+]\n30\n31\n32\n33</p>\n<p>30</p>\n<p>I&#39;m sensing strange resonance between &quot;Every reference remembers the path of the object it&#39;s pointing to.&quot; and generational references where &quot;every reference remembers the ID (&quot;generation&quot;) of the object it&#39;s pointing at.&quot; It <em>could</em> be said that group borrowing is a sort of compile-time generational reference. But more on that in the next article!</p>\n<p>31</p>\n<p>We relax this later with reference counting.</p>\n<p>32</p>\n<p>You could also say that main&#39;s stack frame owns foo&#39;s stack frame. Or you could say the reverse (that&#39;s how async works).</p>\n<p>33</p>\n<p>Or more specifically, responsible for freeing it, or responsible for giving it to someone who will make sure it&#39;s eventually freed.</p>\n<h3 id=\"paths\">Paths</h3>\n<p>Every object can be described by a path.</p>\n<p>For this example:</p>\n<p>valen</p>\n<pre><code>struct Entity { hp int; }\nstruct World {\n  diameter int;\n  entities Vec&lt;Entity&gt;;\n}\nfunc main() int {\n  let x = 42;\n  let world = World(73, Vec&lt;Entity&gt;.new());\n\n  ...\n}</code></pre>\n<p>Here are all the paths in this program:</p>\n<ul><li>x</li><li>world</li><li>world.diameter</li><li>world.entities</li><li>world.entities[]</li><li>world.entities[].hp</li></ul>\n<p>A path always starts with one of these:</p>\n<ul><li>Local variable (like world).</li><li>A function parameter.</li><li>A function&#39;s &quot;group parameter&quot; (we&#39;ll see this later).</li></ul>\n<p>After that start, it will have things like:</p>\n<ul><li>.diameter to name a member (like above).</li><li>[] to name the <em>elements</em> of a collection (like above).</li><li>.SomeType to name a union&#39;s variant. 34</li><li>plus a few other options that I&#39;ll get into later. 35</li></ul>\n<p>Every piece of data has only one owner (because single ownership), so every object has one path. 36</p>\n<p>So what are paths used for?</p>\n<p>References!</p>\n<p>The compiler knows what path each reference points to, and that helps uphold memory safety.</p>\n<p>Notes [–]\nNotes [+]\n34\n35\n36</p>\n<p>34</p>\n<p>So for example, if we have a union/enum Engine containing either a WarpEngine or a ImpulseEngine, the path might be my_engine.WarpEngine.</p>\n<p>35</p>\n<p>Such as an associated group, or sub-types of a multi-typed group. More on these later!</p>\n<p>36</p>\n<p>This isn&#39;t <em>strictly</em> true; objects can actually be reached through multiple paths, if we use things like associated groups. That&#39;s a whole topic for another time though.</p>\n<h3 id=\"every-reference-knows-what-path-it-points-to\">Every reference knows what path it points to</h3>\n<p>For example, in this program:</p>\n<p>valen</p>\n<pre><code>struct Entity { hp int; }\nstruct World {\n  entities Vec&lt;Entity&gt;;\n}\nfunc main() int {\n  let world = World(Vec&lt;Entity&gt;.new());\n\n  world.entities.push(Entity(42));\n\n  let entity_ref = &amp;world.entities[0];\n\n  ...\n}</code></pre>\n<p>entity_ref&#39;s type is &amp;Entity in world.entities[].</p>\n<p>That means it&#39;s a reference to one of world&#39;s entities&#39;s elements.</p>\n<p>And in the example we saw before:</p>\n<p>valen</p>\n<pre><code>struct World {\n  entities Vec&lt;Entity&gt;;\n}\nfunc step(world &amp;World, entity in world.entities[] mut) {\n  entity.advance();\n  let collision = world.get_collision_for_entity(entity);\n  entity.resolve(collision);\n}</code></pre>\n<p>You can see the entity in world.entities[], specifying an entity parameter that points at one of world&#39;s entities&#39;s elements.</p>\n<p>If you know Rust: a path isn&#39;t really like a lifetime. It&#39;s easier to grok when you think about them as different things. 37</p>\n<p>Now let&#39;s find out why we&#39;re doing all of this. Let&#39;s see how we use paths for memory safety!</p>\n<p>Notes [–]\nNotes [+]\n37</p>\n<p>37</p>\n<p>In practice a lifetime can be used as a very coarse-grained location, but a lifetime is more like a span of instructions.</p>\n<h3 id=\"using-paths-for-memory-safety\">Using paths for memory safety</h3>\n<p>The compiler uses paths to <strong>detect use-after-free;</strong> it detects when we&#39;re dereferencing a reference that is pointing at data that has already been destroyed.</p>\n<p>It does this with these two rules:</p>\n<ul><li>When we modify an object, we mark any reference pointing <em>inside</em> that object as &quot;invalidated&quot;.</li><li>When we try to use an &quot;invalidated&quot; reference, the compiler shows an error.</li></ul>\n<p>Take a look at this main function.</p>\n<p>valen</p>\n<pre><code>struct Entity { hp int; }\nstruct World {\n  entities Vec&lt;Entity&gt;;\n}\nfunc main() int {\n  let world = World(Vec&lt;Entity&gt;.new());\n\n  world.entities.push(Entity(42));\n  let first_ref = &amp;world.entities[0];\n\n  world.entities = Vec&lt;Entity&gt;.new();\n\n  // Compile error: Used a borrow after invalidated\n  return first_ref.hp;\n}</code></pre>\n<p>In it, these two things happened:</p>\n<ul><li>At world.entities = ..., the compiler invalidated all references pointing inside it (world.entities[]); it invalidated first_ref.</li><li>At return first_ref.hp, the compiler sees we&#39;re using first_ref, so shows an error.</li></ul>\n<p>Note how I said:</p>\n<p>When we modify an object, we mark any reference pointing <em>inside</em> that object as &quot;invalidated&quot;.</p>\n<p>We don&#39;t invalidate references pointing <em>to</em> an object. We invalidate references pointing <em>inside</em> the object.</p>\n<p>Take a look at this main function.</p>\n<p>valen</p>\n<pre><code>struct Ship { fuel int; }\nfunc main() int {\n  let vec = Vec&lt;Ship&gt;.new();\n  vec.push(Ship(42));\n\n  let vec_ref = &amp;vec;\n  let elem_ref = &amp;vec[0];\n\n  // Mutate `vec`.\n  // Don&#39;t invalidate `vec_ref`.\n  // Invalidate `elem_ref`.\n  vec.push(Ship(73));\n\n  // Can still use `vec_ref`.\n  vec_ref.push(Ship(81));\n\n  // Can&#39;t use `elem_ref`,\n  // Compile error: Used a borrow after invalidated\n  return elem_ref.fuel;\n}</code></pre>\n<p>When we call push, that modifies the vec.</p>\n<p>That invalidates all references pointing <em>inside</em> vec.</p>\n<p>That means that elem_ref is invalidated (though vec_ref is still valid).</p>\n<p>So we can still use vec_ref, but we can&#39;t use elem_ref.</p>\n<p>Notice how this is all happening without describing whether a reference is mutable or not.</p>\n<p>Valen, however, has only <strong>one kind of borrow reference</strong>.</p>\n<p>This can be confusing if you&#39;re coming from C++ and Rust, which both have multiple kinds of references (C++ has const and non-const, Rust has &amp; and &amp;mut).</p>\n<p>In Valen, the mutability of something <strong>isn&#39;t part of its type,</strong> the surrounding function determines it.</p>\n<p>Valen does put mut on function parameters, but that&#39;s syntactic sugar. mut is not part of the parameter&#39;s type. 38</p>\n<p>Notes [–]\nNotes [+]\n38</p>\n<p>38</p>\n<p>mut is part of the function signature. It&#39;s similar to how languages put async on the function, instead of on parameters.</p>\n<h3 id=\"function-signatures-communicate-invalidations\">Function signatures communicate invalidations</h3>\n<p>So how did the compiler know that push modified the vec?</p>\n<p>push has to say so, in its signature, with the mut keyword.</p>\n<p>Here&#39;s push&#39;s signature:</p>\n<p>valen</p>\n<pre><code>func push&lt;T&gt;(self &amp;Vec&lt;T&gt; mut) { ... }</code></pre>\n<p>...which is syntactic sugar for this:</p>\n<p>valen</p>\n<pre><code>func push&lt;T&gt;(self &amp;Vec&lt;T&gt;) mut(self) { ... }</code></pre>\n<p>Notice how mut(self) isn&#39;t part of self&#39;s type. It&#39;s more part of the <em>function.</em></p>\n<p>So when main called vec.push(...), main was passing vec in for self.</p>\n<p>The compiler then sees the mut(self), knows self is actually vec, so knows vec was mutated.</p>\n<p>That&#39;s how it knew to invalidate anything pointing <em>inside</em> vec.</p>\n<p>Here&#39;s another example:</p>\n<p>valen</p>\n<pre><code>func step(world &amp;World, entity in world.entities[] mut) {\n  entity.advance();\n  let collision = world.get_collision_for_entity(entity);\n  entity.resolve(collision);\n}</code></pre>\n<p>We&#39;re not mutating the whole world; we&#39;re only mutating something in world.entities[].</p>\n<p>This is good because this won&#39;t invalidate callers&#39; references to entities.</p>\n<p>For example, this step_and_read is calling the above step function which <em>doesn&#39;t</em> invalidate the entity.</p>\n<p>valen</p>\n<pre><code>func step_and_read(world &amp;World, entity in world.entities[] mut) {\n  // Only modifies an entity&#39;s contents.\n  step(world, entity);\n\n  // ...so can still use `entity`!\n  entity.read_book();\n}</code></pre>\n<p>And if we craft the Valen compiler right (and patch LLVM), this <em>might</em> let us harness the optimizer in a way that no language ever has before. 39</p>\n<p>Notes [–]\nNotes [+]\n39</p>\n<p>39</p>\n<p>I talk more about this further below, but TL;DR, if the optimizer knows that two functions only overlap in reads and not writes, then it can reorder them, perhaps even hoisting calculations out of a loop.</p>\n<h2 id=\"a-twist-on-group-borrowing\">A twist on group borrowing</h2>\n<p>The original group borrowing designs included a lot of the above (and more!). Here&#39;s the things that Valen will be adding on top of it, to make it <em>sing.</em></p>\n<h3 id=\"mutable-in-immutable\">Mutable-in-Immutable</h3>\n<p>Recall this program, where we took in a world that was immutable <em>except</em> for its entities elements:</p>\n<p>valen</p>\n<pre><code>struct World {\n  entities Vec&lt;Entity&gt;;\n}\nfunc step(world &amp;World, entity in world.entities[] mut) {\n  entity.advance();\n  let collision = world.get_collision_for_entity(entity);\n  entity.resolve(collision);\n}</code></pre>\n<p>We&#39;re receiving two parameter references, where we can mutate through the more specific one, and we can&#39;t mutate anything outside of it.</p>\n<p>Valen adds this so that we don&#39;t have to do the classic borrow-checking pattern of taking in the entire world mutably.</p>\n<p>This will have a couple benefits.</p>\n<p>First, it could help with a program&#39;s architecture, because it helps us make <strong>stronger APIs.</strong></p>\n<p>We can enforce that nothing in this entire function can change any part of the world that we don&#39;t expect.</p>\n<p>I imagine that will be particularly helpful when working in a team with enterprising newhires (or with product managers who love to throw vibe code at you).</p>\n<p>Second, I suspect this feature could make Valen code <strong>optimize better.</strong></p>\n<p>I won&#39;t go too deeply into it here, 40 but basically, when you give the optimizer more fine-grained information about what might change and what won&#39;t change, it can better identify places where it can rearrange code to be faster. 41 42</p>\n<h3 id=\"temporary-uniqueness\">Temporary Uniqueness</h3>\n<p>One of Valen&#39;s goals is to be able to <strong>seamlessly call into Rust code</strong>, as easily as Kotlin calls into Java, or C++ into C.</p>\n<p>Of course, this will be tricky, because group borrowing and Rust have different kinds of references:</p>\n<ul><li>Group borrowing allows others to change the data that your reference is pointing at.</li><li>In Rust, every reference is either shared (&amp;) or unique (&amp;mut). When you hold a reference, nobody else can change what your reference is pointing at.</li></ul>\n<p>So the question that arose was: Can we turn a group borrowing reference into a Rust reference, temporarily?</p>\n<p>It turns out, yes!</p>\n<p>The mechanism is pretty simple. When Valen is calling Rust:</p>\n<ul><li>We can hand a Valen reference into a Rust &amp;mut if it&#39;s the <em>only</em> reference that can reach that data during that call.</li><li>If we have multiple Valen references that can point to the same object, they can only be handed into Rust &amp; references.</li></ul>\n<p>I talk a little bit about this in <a href=\"https://verdagon.dev/blog/boundary-memory-safety#temporary-uniqueness-and-noalias\" rel=\"nofollow ugc noopener\">Memory Safety Across the Valen/Rust Boundary</a>, check it out!</p>\n<p>This temporary conversion <em>could</em> evolve into a broader feature one day. We want Valen to support something like Rust&#39;s Vec&lt;&amp;mut Ship&gt;, where each element is a unique reference, and group borrowing doesn&#39;t have that.</p>\n<p>I suspect Valen can have something like a &quot;unique group&quot;, e.g. a unique(world.ships[]) annotation on a function, which would prevent making new references pointing at that part of the hierarchy.</p>\n<p>This aspect needs more thought though. One of Valen&#39;s greatest strengths is that it only has one kind of borrow reference... so introducing a second &quot;unique&quot; reference requires some careful consideration.</p>\n<p>Notes [–]\nNotes [+]\n40\n41\n42</p>\n<p>40</p>\n<p>Ask me on Mastodon or Bluesky and I&#39;m happy to explain!</p>\n<p>41</p>\n<p>This might require patches to LLVM to make it better able to harness the information. It tends to think in terms of noalias, not in terms of &quot;read-only&quot;. Tricky, but possible!</p>\n<p>42</p>\n<p>This won&#39;t necessarily mean Valen will be faster than C++, Rust, etc. I <em>think</em> you can do extra refactoring or use unsafe in those languages to get equivalent speedups. Valen&#39;s main benefit here would be in ergonomics and flexibility, not necessarily speed.</p>\n<h3 id=\"wildcard-descendant-paths\">Wildcard Descendant Paths</h3>\n<p>This is another feature that helps Valen call into Rust code.</p>\n<p>While trying to get the <a href=\"https://verdagon.dev/blog/golden-spike-reviving-vale-valen\" rel=\"nofollow ugc noopener\">Golden Spike</a> to work, I ran into a conundrum.</p>\n<p>When Valen calls a Rust function that returns a reference (like -&gt; &amp;Gem below), how does it know <em>where</em> that reference points?</p>\n<p>For example:</p>\n<p>rs</p>\n<pre><code>impl Chest {\n  pub fn gem(&amp;self) -&gt; &amp;Gem { &amp;self.gem }\n}</code></pre>\n<p>Valen was confused by this, because Valen likes to know the path for every reference.</p>\n<p>In Valen&#39;s perfect world, it would see the above like this:</p>\n<p>valen</p>\n<pre><code>func gem(self &amp;Chest) -&gt; &amp;Gem in self.gem { &amp;self.gem }</code></pre>\n<p>But alas! Valen can&#39;t infer the in self.gem from the Rust code.</p>\n<p>But if you zoom out a bit, the Rust code is trying to express that it&#39;s returning something to <strong>&quot;somewhere inside&quot;</strong> self.</p>\n<p>So, let&#39;s add that concept to Valen!</p>\n<p>Valen now interprets the above Rust function as if it were this:</p>\n<p>valen</p>\n<pre><code>func gem(self &amp;Chest) -&gt; &amp;Gem in self.gem... { &amp;self.gem }</code></pre>\n<p>The in self.gem... means &quot;points <em>somewhere inside</em> self.gem&quot;.</p>\n<p>This is called a <strong>wildcard descendant path</strong>, and it definitely needs a better name.</p>\n<p>I describe this a little more in <a href=\"https://verdagon.dev/blog/boundary-memory-safety#temporary-uniqueness-and-noalias\" rel=\"nofollow ugc noopener\">Memory Safety Across the Valen/Rust Boundary</a>.</p>\n<p>I suspect this will be useful for more than just Rust interop. It could erase inner details from signatures, and make library APIs a little more flexible and forward-compatible. However, we should be careful here too, because that same purpose can be served by other features in theory. 43</p>\n<p>Notes [–]\nNotes [+]\n43</p>\n<p>43</p>\n<p>For example, we could have a type publicly re-export a private group under a public name. Something like an &quot;associated group&quot;, so to speak.</p>\n<h2 id=\"how-this-all-fits-in\">How this all fits in</h2>\n<p>Above, I mentioned the &quot;memory safety holy grail&quot;, and the <strong>three challenges</strong> that we need to solve to find it:</p>\n<ul><li>Can we make a <em>more relaxed</em> borrow checker?</li><li>Can we make a borrow checker read <em>more complex data?</em></li><li>Can we make those two systems work together at the same time?</li></ul>\n<p>Valen&#39;s borrow checker is the key to #1: it lets us have multiple references to any object, and lets us read and write through any of them.</p>\n<p>#2 and #3 will be in another post (since this post is getting massive!) but I can&#39;t resist giving a <strong>quick preview of what it&#39;s all about.</strong></p>\n<p>These are my hasty scribbles to try and explain it in half a page. Nobody should try to understand it. Quick, jump ahead!</p>\n<p>Recall this example from above:</p>\n<p>valen</p>\n<pre><code>struct World {\n  entities Vec&lt;Entity&gt;;\n}\nfunc step(world &amp;World, entity in world.entities[] mut) {\n  ...\n}</code></pre>\n<p>I want users to have the option of using reference counted classes when they don&#39;t want to specify the paths. Like this:</p>\n<p>valen</p>\n<pre><code>class World {\n  entities Vec&lt;Entity&gt;;\n}\nfunc step(world World, entity Entity) mut {\n  ...\n}</code></pre>\n<p>It would be amazing to give the users the freedom to simplify like that. 44</p>\n<p>Of course, blending reference counting and borrowing is <strong>infamously difficult.</strong> 45</p>\n<p>The answer lies in a thought experiment from 2023 I called <a href=\"https://verdagon.dev/blog/myth-zero-overhead-memory-safety#arrrlang-a-pirate-themed-language-with-zero-overhead-memory-safety\" rel=\"nofollow ugc noopener\">Arrrlang</a>. Its name was a joke, but it had an interesting premise: represent the heap as N global arrays, where N is the number of types in your program. 46</p>\n<p>It occurred to me: group borrowing <em>understands</em> arrays. And reference counting can be thought of as N arrays, all having references to each other.</p>\n<p>If we put those two facts together, then the key insight emerges: if we <strong>describe reference-counted classes to a borrow checker as if it&#39;s a bunch of arrays,</strong> then we might be able to have borrow references into them in a memory-safe way.</p>\n<p>A compiler doesn&#39;t need to <em>actually compile</em> reference counted objects to an array, of course. <strong>But it <em>can</em> describe it that way to the borrow checker,</strong> using group-borrowing terms.</p>\n<p>If you do it that way, suddenly a lot of problems disappear, and it clarifies exactly what building blocks we need to add to group borrowing:</p>\n<ul><li>A &quot;multi-typed group&quot;, similar to a Vale region, as opposed to vanilla group borrowing where every group needs a type. 47 48</li><li>A &quot;run-time&quot; group. References into the run-time group don&#39;t get invalidated.</li></ul>\n<p>Like I said, none of this will make sense. Keep an eye out for the next post that talks about all this!</p>\n<p>Notes [–]\nNotes [+]\n44\n45\n46\n47\n48</p>\n<p>44</p>\n<p>Reference counting has a lot less restrictions (e.g. references don&#39;t need to be temporary, like in borrow checking), which means you unlock a lot more patterns (observers, intrusive linked lists, etc). It also means nobody can free an object while you&#39;re accessing it, which is most often a good thing.</p>\n<p>But it depends on your perspective. Reference counting occasionally has problems with cycles, and it&#39;s not very compatible with linear types. For most things, I like reference counting. For the more complex parts, or parts that need speed, I like borrowing.</p>\n<p>I think it&#39;s good for a language to support both.</p>\n<p>45</p>\n<p>It&#39;s difficult for all the same reasons as why Rc&lt;RefCell&lt;T&gt;&gt; has that RefCell in there.</p>\n<p>46</p>\n<p>It was an example of a &quot;zero-cost&quot; language that actually had some cost, in the form of ID-chasing and bounds checking.</p>\n<p>47</p>\n<p>This will also be useful for enums!</p>\n<p>48</p>\n<p>Fun fact, this is how they represent heaps under the hood in <a href=\"https://iris-project.org/pdfs/2015-popl-iris1-final.pdf\" rel=\"nofollow ugc noopener\">Iris</a>, which was used to formally verify that parts of Rust are safe.</p>\n<h2 id=\"that-s-all-for-now\">That&#39;s all for now</h2>\n<p>Thanks for reading!</p>\n<p>In the next few articles, I&#39;ll explain more about how we&#39;ll blend in reference counting and generational references, and how Valen&#39;s group borrow checker is implemented.</p>\n<p>This is just the tip of the iceberg, so stay tuned by subscribing to my <a href=\"https://verdagon.dev/rss.xml\" rel=\"nofollow ugc noopener\">RSS feed</a>, <a href=\"https://www.reddit.com/r/Valen/\" rel=\"nofollow ugc noopener\">r/valen</a>, or joining the <a href=\"https://discord.gg/SNB8yGH\" rel=\"nofollow ugc noopener\">Valen discord</a>. And follow me on <a href=\"https://bsky.app/profile/verdagon.bsky.social\" rel=\"nofollow ugc noopener\">Bluesky</a> and <a href=\"https://fosstodon.org/@verdagon\" rel=\"nofollow ugc noopener\">Mastodon</a>!</p>\n<p>Cheers,</p>\n<ul><li>Evan Ovadia</li></ul>\n<h2 id=\"appendix-the-type-stability-exception\">Appendix: The type-stability exception</h2>\n<p>This part isn&#39;t implemented in Valen yet. Stay tuned!</p>\n<p>Group borrowing can also be smart enough to know that if you say my_vec.push(42), then even though push says mut(self), it won&#39;t invalidate pointers to my_vec.size.</p>\n<p>This makes sense because no matter how anyone modifies a my_vec (which is a Vec), the thing at my_vec.size will still be there. Nobody can modify a Vec&#39;s size to be anything other than an integer. In other words, it&#39;s &quot;type-stable&quot;.</p>\n<p>Generalizing a bit, group borrowing will never invalidate any type-stable data.</p>\n<p>It won&#39;t invalidate pointers to any contained type-stable things (primitives, structs, or fixed-size arrays), or any type-stable things inside them. It only invalidates pointers pointing inside any type-unstable things (enums&#39; contents, pointer&#39;s pointee).</p>\n<p>For example, in this program, nobody modifying the contents of Level can invalidate your reference to its Terrain.</p>\n<p>valen</p>\n<pre><code>struct Level {\n  terrain Terrain;\n  entities HashMap&lt;EntityId, Entity&gt;;\n  location_to_entity HashMap&lt;Location, EntityId&gt;;\n}\nfunc add_entity(level &amp;Level mut, entity Entity) {\n  level.location_to_entity.insert(entity.loc, entity.id);\n  level.entities.insert(entity.id, entity);\n}\nfunc main() {\n  let level = ...;\n  let terrain_ref = &amp;level.terrain;\n\n  // Doesn&#39;t invalidate terrain_ref!\n  level.add_entity(Entity(1, Loc(4, 5), 42));\n\n  print(terrain_ref.tiles.len());\n}</code></pre>","headings":[{"level":1,"text":"Valen's Memory Safety: A New Kind of Borrow Checking","id":"valen-s-memory-safety-a-new-kind-of-borrow-checking"},{"level":3,"text":"Group borrowing: a near miss, and huge potential","id":"group-borrowing-a-near-miss-and-huge-potential"},{"level":2,"text":"What group borrowing can do","id":"what-group-borrowing-can-do"},{"level":3,"text":"A more interesting example","id":"a-more-interesting-example"},{"level":2,"text":"How Valen's borrow checker works","id":"how-valen-s-borrow-checker-works"},{"level":3,"text":"Single Ownership","id":"single-ownership"},{"level":3,"text":"Paths","id":"paths"},{"level":3,"text":"Every reference knows what path it points to","id":"every-reference-knows-what-path-it-points-to"},{"level":3,"text":"Using paths for memory safety","id":"using-paths-for-memory-safety"},{"level":3,"text":"Function signatures communicate invalidations","id":"function-signatures-communicate-invalidations"},{"level":2,"text":"A twist on group borrowing","id":"a-twist-on-group-borrowing"},{"level":3,"text":"Mutable-in-Immutable","id":"mutable-in-immutable"},{"level":3,"text":"Temporary Uniqueness","id":"temporary-uniqueness"},{"level":3,"text":"Wildcard Descendant Paths","id":"wildcard-descendant-paths"},{"level":2,"text":"How this all fits in","id":"how-this-all-fits-in"},{"level":2,"text":"That's all for now","id":"that-s-all-for-now"},{"level":2,"text":"Appendix: The type-stability exception","id":"appendix-the-type-stability-exception"}]}}