The Second Golden Spike: Memory Safety Across the Valen/Rust Boundary

Lobsters Hottest Tools

Summary

Verdagon details how the experimental Valen programming language enforces memory safety across the Valen/Rust boundary, implementing group borrowing (borrow checking without shared-xor-mutable) while seamlessly calling into Rust functions directly.

<p><a href="https://lobste.rs/s/ajtnc6/second_golden_spike_memory_safety_across">Comments</a></p>
Original Article
View Cached Full Text

Cached at: 10/01/26, 02:26 PM

# The Second Golden Spike: Memory Safety Across the Valen/Rust Boundary Source: [https://verdagon.dev/blog/boundary-memory-safety](https://verdagon.dev/blog/boundary-memory-safety) Making two memory\-safety worlds meet Oct 1, 2026— In[The Golden Spike, and Resurrecting the Vale\(n\) Programming Language](https://verdagon.dev/blog/golden-spike-reviving-vale-valen), we saw how it was possible for a language to call Rust functions directly, without going through C, by directly integrating with the Rust compiler\.[0](https://verdagon.dev/blog/boundary-memory-safety#note0)[1](https://verdagon.dev/blog/boundary-memory-safety#note1) It was one hell of a journey\! At the end, I managed to make the "golden spike", a Valen program that uses a Rust graphics library: In there, I hinted about how we're doing borrow checking between Valen and Rust: This post is long so I'll save the details for the next one, but TL;DR: it's not complete yet, but this prototype is doing some borrow checking over the boundary\! Welcome to that next post\! In this post, I'll show you how we can uphold memory safety across the Valen/Rust boundary\. Plus, we'll see a**certain surprise**that happened as I was building it, which led to me**breaking a foundational law of the universe\.** ### Valen \(If you read the[Golden Spike](https://verdagon.dev/blog/golden-spike-reviving-vale-valen),[skip this section](https://verdagon.dev/blog/boundary-memory-safety#valens-memory-safety-group-borrowing)\) This is all done in the new[Valen](https://github.com/valen-lang/Valen)programming language \(successor to[Vale](https://vale.dev/)\)\. It's a very early, experimental language that:[2](https://verdagon.dev/blog/boundary-memory-safety#note2) - Calls into Rust functions \(including generic ones\!\) seamlessly without bindings or wrappers\. - Uses a**more flexible borrow checker without shared\-xor\-mutable\.** - Has**linear types**for resource safety and[Higher RAII](https://verdagon.dev/blog/higher-raii-uses-linear-types)\. It also has plans to add: - Zig\-style comptime, so we can do things like[the impossible optimization](https://verdagon.dev/blog/impossible-optimization)\. - Generational references[3](https://verdagon.dev/blog/boundary-memory-safety#note3) - An Rc that can hold mutable data withoutRefCell/Cell/Mutex\. - Plus*much*more\.[4](https://verdagon.dev/blog/boundary-memory-safety#note4) This entire endeavor is*extremely experimental*and many things have holes and sharp edges \(see side\-note[5](https://verdagon.dev/blog/boundary-memory-safety#note5)\)\. It's going to be a glorious few months of cleaning, rewriting, and solidifying this horror before I unleash it on the world\. Feel free to check out the[Valen repo](https://github.com/valen-lang/valen)and beware, here be dragons\![6](https://verdagon.dev/blog/boundary-memory-safety#note6) One could call it a Rust\+\+, since it can seamlessly call into existing Rust code\. Or it could be more of a Rust\-\-, because it aims to be simpler, andoffers an easier way to use Rust code\. We'll see some of that further below, when it lets us use existing Rust code in a more flexible way than Rust itself\. Let's talk about Valen's memory safety approach\! ### Valen's Memory Safety: Group Borrowing This is something I'm*ridiculously*excited about for Valen\. At long last, Valen has an implementation of Nick Smith's[Group Borrowing](https://verdagon.dev/blog/group-borrowing)memory safety approach\. Group borrowing is similar to Rust's borrow checking, but without the shared\-xor\-mutable restriction\. This means that Valen functions can do things like: - Have local variables that point to the same object, and write through any of them\. - Take multiple parameters that point to the same object, and write through them\. - Take a parameter pointing at an object, and another parameter pointing somewhere*inside*that object, and write only the latter\. This comes with a*lot*of simplifying powers\. For example, here's a Rust function, and below I'll show what Valen can simplify it to\. ``` // Make attacker_id entity attack defender_id entity. // Note: Attacker might be defender (attacking self). fn attack( entities: &mut SlotMap<DefaultKey, Entity>, attacker_id: DefaultKey, defender_id: DefaultKey ) -> Result<(), String> { let a = entities .get(attacker_id) .ok_or_else(|| "Attacker not found in entities map".to_string())?; let d = entities .get(defender_id) .ok_or_else(|| "Defender not found in entities map".to_string())?; let a_energy_cost = a.calculate_attack_cost(d); let d_energy_cost = d.calculate_defend_cost(a); let damage = a.calculate_damage(d); let a_mut = entities .get_mut(attacker_id) .ok_or_else(|| "Attacker not found in entities map".to_string())?; a_mut.use_energy(a_energy_cost); let d_mut = entities .get_mut(defender_id) .ok_or_else(|| "Defender not found in entities map".to_string())?; d_mut.use_energy(d_energy_cost); d_mut.damage(damage); Ok(()) } ``` The above becomes this Valen function:[7](https://verdagon.dev/blog/boundary-memory-safety#note7) ``` // Make attacker_id entity attack defender_id entity. // Note: Attacker might be defender (attacking self). func attack(a &Entity in g, d &Entity in g) mut(g) { let a_energy_cost = a.calculate_attack_cost(d); let d_energy_cost = d.calculate_defend_cost(a); let damage = a.calculate_damage(d); a.use_energy(a_energy_cost); d.use_energy(d_energy_cost); d.damage(damage); } ``` A huge difference\![8](https://verdagon.dev/blog/boundary-memory-safety#note8)[9](https://verdagon.dev/blog/boundary-memory-safety#note9) This is possible because group borrowing lets us have**multiple mutable references to the same object\.** This is what the Rust code was*trying*to express, but Rust's borrow checker doesn't allow it\.[10](https://verdagon.dev/blog/boundary-memory-safety#note10) Specifically, Rust's borrow checker has the**shared\-xor\-mutable**restriction, which says: - Each reference must be read\-only or read\-write\.[11](https://verdagon.dev/blog/boundary-memory-safety#note11) - If you have a read\-write reference to an object, you can't use any other references to it\. - If you have a read\-only reference to an object, you can't write through it\.[12](https://verdagon.dev/blog/boundary-memory-safety#note12) Group borrowing instead**tells the compiler**when two references might be pointing at the same thing, and then uses**invalidation tracking**to make sure that we don't use a reference that might be dangling\.[13](https://verdagon.dev/blog/boundary-memory-safety#note13) You can almost think of it like a compile\-time version of[generational references](https://verdagon.dev/blog/generational-references)\(in fact, that's close to how it's implemented in the compiler\!\) If you want to know more about group borrowing, check out the original[group borrowing post](https://verdagon.dev/blog/group-borrowing)\. For now, just know that it's more flexible because it allows multiple references to an object, and lets us**write through any of them\.** Now for the surprising part: Valen can also do that with Rust objects\. In other words, we can have multiple mutable references to Rust objects, safely\. I can hear some of you going,*"Wait, what?\!"* The Second Golden Spike: Memory Safety Across the Valen/Rust Boundary 0 0% of this article was written by AI\.[More thoughts here](https://verdagon.dev/blog/personal-ai-policy)\. My stance: if you want someone to take the time to read something, take the time to write it by hand\. Thanks for reading =\) 1 Also thanks to Let's Get Rusty, who made a[youtube video](https://www.youtube.com/watch?v=jJJm2nQVolY)on the Golden Spike article\. It's a great overview, check it out\! And one correction: I don't work for Mojo anymore\. They definitely wouldn't have let me do any of this\! 2 The main differences between Valen and Vale: - Valen will have[group borrowing](https://verdagon.dev/blog/group-borrowing), Vale had[region borrowing](https://verdagon.dev/blog/generational-references)\. - Valen will have normal generational references, Vale had probabilistic generational references\. - Valen will have \(and Vale didn't have\) Rust interop, Rc, comptime\. - Valen won't have \(and Vale did have\) perfect replayability, fearless FFI\. Both have linear types\. Their syntax is still mostly the same, but Valen might change to be more of a "simpler Rust" syntactically\. 3 The normal ones, not the Vale\-style[random generational references](https://verdagon.dev/blog/generational-references#random-generational-references)\. 4 Things like: a betterasyncstory, better enums, better[mustprogress](https://llvm.org/docs/LangRef.html)optimizations, closures implementing traits, universal function call syntax, good compile times, and*maybe*if we're lucky we can bring back some subset of[perfect replayability](https://verdagon.dev/blog/perfect-replayability-prototyped)\. 5 To be clear about what works today: - Valen has linear types, but we can't yet declare that an existing Rust type is linear\. - Valen has group borrowing \(except for closures\), and it borrow checks across the boundary\. - Structs work across the boundary, even Valen structs that implement Rust traits\. But they must be zero\-sized \(filled structs work in my separate prototype, not yet in Valen\)\. - Generational references are temporarily disabled, hopefully coming back soon\. 6 Green dragons, specifically\. 7 The biggest takeaway is that thein gon the references tells the compiler that the references might point at the same object\. Themut\(g\)also says that we might mutate it\. To really know what's going on here, check out the[Group Borrowing](https://verdagon.dev/blog/group-borrowing)article\. 8 Also, if you look very closely, you'll see that the Valen example is probably faster, because it doesn't need all the error\-handling branches, and instead of doing 4 lookups like the Rust version, it's doing zero \(or two, because we might have just moved them out to the caller, depending on circumstances\)\. Take that with a grain of salt though, I haven't benchmarked it yet\. If true, that'll be a wild post when it comes\. 9 I like this example because it's the clearest, but I've come across more common cases where group borrowing can make something simpler than normal borrow checking\. See[this section](https://verdagon.dev/blog/boundary-memory-safety#thoughts-on-group-borrowings-flexibility)for more examples\. 10 It might seem like we could do this withCell, but that makes a lot of assumptions about what's inside these methods\. Depending on how complicateduse\_energyanddamageare, using aCellmight not be such a great idea\.GhostCellmight be a better option for this example\. There are other group\-borrowing examples thatGhostCellwouldn't quite work for though, I wouldn't consider it equivalent\. 11 More specifically, a reference must be "shared" or "unique"\. 12 We can technically still mutate through a Rust shared reference, if it has anUnsafeCellsomewhere inside it\. This usually comes with a tradeoff, such as not being able to make references to some things inside it \(Cell\), run\-time errors \(RefCell\) orunsafe\. 13 "Dangling" means pointing at something that has already been destroyed\. ### Breaking a foundational law of the universe That's right, Valen code can**safely have multiple mutable references to Rust objects\.** The Rust half of my brain can't even comprehend how blasphemous this is\.[14](https://verdagon.dev/blog/boundary-memory-safety#note14)The mad sorcerer half laughs at the Rust half's discomfort\.[15](https://verdagon.dev/blog/boundary-memory-safety#note15) And yet, it works\! ``` import slotlib.Slot; exported func main() int { let slot = Slot.new(); let ref_a = &slot; let ref_b = &slot; ref_a.mutate(42); ref_b.mutate(73); return ref_a.get(); } ``` ``` pub struct Slot { value: i32, } impl Slot { pub fn new() -> Slot { ... } pub fn get(&self) -> i32 { self.value } pub fn mutate(&mut self, x: i32) { self.value = x; } } ``` Note howref\_aandref\_bare both pointing to the sameSlot, yetref\_a\.mutate\(42\);is handing one of those references into a Rust&mutreference\. But to understand why that works, let's first talk about**temporarily unique references**and "noalias", and then I'll explain how they help us call into Rust functions like we did above\. 14 If you've used Rust before, this is surprising because one of the central tenets of Rust is that there can only be*one*mutable reference to an object at a time\. But we all knew that that was an imprecise model, that's what people mean when they say that Rust's borrow checker is conservative\. \(Don't get me wrong, group borrowing is also conservative, just not as much as Rust in this case\) 15 And the third half laughs at how the first two are always bickering over what's possible and/or sane\. ### Temporary Uniqueness and noalias A related question I'm often asked is: "Wait, Verdagon, Rust references are more optimizable because they become noalias pointers in LLVM\. If Valen references can alias each other, does that make it slower?" A good question, and understanding the answer also helps us understand the Valen/Rust boundary\. **TL;DR:**Valen uses noalias too, so it's just as fast\. I'll explain the question a bit more, and then explain how Valen does it\. #### What is noalias? In Rust, when you have a reference parameter pointing at some data,[16](https://verdagon.dev/blog/boundary-memory-safety#note16)you know that**nobody else is writing to that data**\.rustcmakes those into LLVM noalias pointers, so LLVM knows about that, and can optimize better\. This is similar to C'srestrictpointers, which also become LLVM noalias pointers\. For example, here's a C function withoutrestrict: ``` void addMuchRandom(int *destination, struct Rand *random_state) { for (int i = 0; i < 100; i++) *destination += next_random(random_state); } ``` LLVM can't optimize this well\.[17](https://verdagon.dev/blog/boundary-memory-safety#note17) However, if we make that parameterint \*restrict destination, like this: ``` void addMuchRandom(int *restrict destination, struct Rand *random_state) { for (int i = 0; i < 100; i++) *destination += next_random(random_state); } ``` \.\.\.then it can be optimized better, because the C compiler makes it into an LLVMnoaliaspointer\.[18](https://verdagon.dev/blog/boundary-memory-safety#note18) Now LLVM can optimize the function to something like this:[19](https://verdagon.dev/blog/boundary-memory-safety#note19)[20](https://verdagon.dev/blog/boundary-memory-safety#note20) ``` void addMuchRandom(int *restrict destination, struct Rand *random_state) { register int temp_register = *destination; for (int i = 0; i < 100; i++) temp_register += next_random(random_state); *destination = temp_register; } ``` This is faster because we**load from the pointer once**at the beginning and store into it once at the end, instead of in every iteration\. So Crestrictpointers are a lot like Rust references\. With both, nobody else is writing to that data, and both become LLVM noalias pointers, which are optimized better\. 16 Note this happens for shared references toFreezedata \(which means it doesn't contain anyUnsafeCell,Cell,RefCell, etc\.\) or unique references\. 17 It becomes this[unoptimized LLVM IR](https://godbolt.org/z/1WsdaGEbd), which optimizes to this[optimized LLVM IR](https://godbolt.org/z/P8hM6fW57), which isn't very optimal, because we're still doing a load and store in every iteration\. 18 It becomes this[LLVM IR](https://godbolt.org/z/3KMxoq46T)with noalias which, when optimized, becomes this[more optimal](https://godbolt.org/z/b6abqvoWo) 19 Withrestrict, LLVM can lift the loads and stores out of the loop\. Before, withoutrestrict, LLVM knows that thatint\* destinationmight be pointing*anywhere*\.\.\. including intorandom\_state\-\>state\_int, in which case lifting the loads and stores would cause weird behavior\. restrictpromises LLVM that it won't be called like that\. 20 This isn't exactly how theregisterkeyword works in C, but you get the idea\. #### How does Valen make LLVM noalias pointers? Now let's look at our example Valen function again, plus thedamagemethod: ``` func attack(a &Entity in g, d &Entity in g) mut(g) { ... d.damage(damage); } func damage(entity &Entity mut, amount int) { entity.hp = max(0, entity.hp - amount); } ``` The question breaks down into three questions: 1. Isdamage's parameterentitymarked noalias? \(Hint: yes\) 2. Areattack's parametersaanddmarked noalias? \(Hint: no\) 3. Can thed\.damage\(damage\)call hand a non\-noalias reference \(d\) to a noalias parameter? \(Hint: yes\) **Q1: Isdamage's parameterentitymarked noalias?** Yep, it is\. When a reference is the*only reference into its group*, Valen makes it into an LLVM noalias pointer\. So in thisdamagefunction: ``` func damage(entity &Entity mut, amount int) { entity.hp = max(0, entity.hp - amount); } ``` \.\.\.which is syntactic sugar for this: ``` func damage<eg'>(entity &Entity in eg, amount int) mut(eg) { entity.hp = max(0, entity.hp - amount); } ``` \.\.\.Valen sees thatentityis the**only reference into the group**eg\. Therefore, it knows that*nobody else is writing to the data that this reference points at\.* Therefore, it can be an LLVM noalias pointer\. **Q2: Areattack's parametersaanddmarked noalias?** Nope, they aren't\. This is intentional, because they do alias\. That's the user's intent, after all\! More specifically, looking at thisattackfunction: ``` func attack(a &Entity in g, d &Entity in g) mut(g) { ``` Valen sees thataisn't the only reference into the groupg, so it can't be marked noalias\. Same withd\. **Q3: Can thed\.damage\(damage\)call hand a non\-noalias reference \(d\) to a noalias parameter?** Quite easily\! This just works™ already in LLVM, because that's how we've been doing it in C for decades\. For example[this C program](https://godbolt.org/z/Eqabjc7Yx)hands an aliased pointer into arestrictpointer parameter, and it's fine\. This is safe, as long as the data the pointer is pointing at isn't being accessed through any other argument\. This is also how Rust works under the hood\! When a Rust type contains anUnsafeCell\(orCell, orRefCell, orGhostCell, etc\.\), shared references to it are*normal*LLVM pointers \(no noalias\)\. Then, usually through a safe API around anunsafeblock, we can get a&mutreference \(LLVM noalias pointer\) out of it\. So, now that we know how Valen can make LLVM noalias pointers, let's finally see how that helps Valen call into Rust functions\! ### Memory safety across the boundary[21](https://verdagon.dev/blog/boundary-memory-safety#note21) Here's the Valen program from above, calling into a Rust library: ``` import slotlib.Slot; exported func main() int { let slot = Slot.new(); let ref_a = &slot; let ref_b = &slot; ref_a.mutate(42); ref_b.mutate(73); return ref_a.get(); } ``` ``` pub struct Slot { value: i32, } impl Slot { pub fn new() -> Slot { ... } pub fn get(&self) -> i32 { self.value } pub fn mutate(&mut self, x: i32) { self.value = x; } } ``` Note howref\_aandref\_bare both pointing to the sameSlot, yetref\_a\.mutate\(42\);is handing one of those references into a Rust&mutreference\. Like we saw above, this is safe because, for the duration of thatmutatecall, only one pointer toslotis being used\. Valen knows that at the callsite because onlyref\_ais being passed intoref\_a\.mutate\(42\)\. You could almost think of it like Valen is makingref\_a**temporarily unique**for the call\. And under the hood, we're passing a normal LLVM pointer \(ref\_a\) into a function that receives it as an LLVM noalias pointer, which is fine for the same reasons[this C program from above](https://godbolt.org/z/Eqabjc7Yx)was fine\. 21 Easter egg note\! **Cricket\-spitting**is a sport where people spit a dead cricket as far as they can\. Whoever can spit the cricket the furthest is declared the winner\. Glorious\! \(Source:[Wikipedia](https://en.wikipedia.org/wiki/Cricket-spitting)\) ### A more advanced case So far, things just seem to work for free, since group borrowing was so similar to Rust\. However, there was a case where group borrowing didn't really understand a reference that came from Rust\. To illustrate, here's an \(unsafe\) Valen program using a Rust methodgemthat it needs to understand: ``` import box_lib.Chest; import box_lib.Gem; exported func main() int { let chest = Chest.new(); let gem_ref = chest.gem(); chest.replace(8); // Should error here return gem_ref.get(); } ``` ``` pub struct Gem { value: i32, } impl Gem { pub fn get(&self) -> i32 { self.value } } pub struct Chest { gem: Box<Gem>, } impl Chest { pub fn new() -> Chest { ... } pub fn gem(&self) -> &Gem { &self.gem } pub fn replace(&mut self, value: i32) { self.gem = Box::new(Gem { value }); } } ``` This Rust function was problematic for Valen: ``` pub fn gem(&self) -> &Gem { &self.gem } ``` \.\.\.because Rust doesn't describe*where*inside self that Gem lives, and Valen definitely wants to know where every reference points\. Valen would*love*it if Rust said this: ``` pub fn gem(&self) -> &Gem in self.gem[] { &self.gem } ``` \.\.\.but alas, we live in that world not\. So, without that information, group borrowing got confused in these lines: ``` // Where does gem_ref even point to? let gem_ref = chest.gem(); // Modifies chest, but what does that do to gem_ref? chest.replace(8); // Is this line even safe? return gem_ref.get(); ``` It was confused because Valen needed to know*where*a reference points, to know whether it's dangling\. So, Valen has to add a new concept on top of group borrowing\. Inspired by Vale's[isolates](https://verdagon.dev/blog/zero-cost-borrowing-regions-part-2-isolates),[22](https://verdagon.dev/blog/boundary-memory-safety#note22)I'm adding a concept called a**wildcard descendant path\.** Valen now sees the Rust function as if it was this: ``` pub fn gem(&self) -> &Gem in self... { &self.gem } ``` Thatself\.\.\.means "*somewhere*inside self"\. Now Valen sees the lines like this: ``` // gem_ref is a `&Gem in chest...` let gem_ref = chest.gem(); // Modifies chest, so invalidates all refs that might point inside `chest` chest.replace(8); // Show an error on this line, because `&Gem in chest...` points inside `chest` return gem_ref.get(); ``` And it works\! It successfully gives a compile error on that line, becausegem\_ref\.get\(\)is trying to do a use\-after\-free\. To summarize, Valen's wildcard descendant paths let it understand references that point "somewhere" inside an object\.[23](https://verdagon.dev/blog/boundary-memory-safety#note23) Quite serendipitously, it closed the gap between Valen and Rust\. With that, Valen's borrow checker could uphold safety across the boundary, and thus the**second golden spike**was complete\. Reminder: Even though all these examples worked, it doesn't yet mean that we've proven Valen to be completely memory safe when calling into Rust, or completely memory safe in general\. Like I said above, this entire endeavor is*extremely experimental*and many things have holes and sharp edges\. We will undoubtedly find problems, and hopefully also find fixes for them\. Stay tuned\! ### That's all for now Thanks for reading\! In the next few articles, I'll explain more about group borrowing, and how Valen's group borrow checker is implemented\. And maybe, if we're lucky, I'll have implemented a third golden spike, which has something to do with Rust and linear types\. This 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)\! Cheers, \- Evan Ovadia 22 In Vale's design, you can open an isolate as a region, and then have a reference to anything inside that region\. Also, Vale regions are un\-typed; in other words, you can have a reference to type X that lives "somewhere inside" object Y's isolate's region\. 23 This was actually already in my plans for Valen, because sometimes we don't want to put an entire path \(like&Gem in self\.gem\[\]\) in a signature, and it's more readable to just say\.\.\.\(like&Gem in self\.\.\.\)\. ### FAQ #### Returning&mut Q: When we get a &mut from a Rust callee, isn't it UB to read/write through another reference pointing at the same data? For example: ``` exported func main() int { let slot = Slot.new(); let ref_a = &slot; let ref_b = &slot; // Is this &mut / noalias? let g = ref_a.get_mut(); // If so, wouldn't this be UB? let x = ref_b.peek(); return g.get() + x; } ``` ``` pub struct Gem { value: i32 } impl Gem { pub fn get(&self) -> i32 { self.value } } pub struct Slot { gem: Gem } impl Slot { pub fn new() -> Slot { ... } pub fn get_mut(&mut self) -> &mut Gem { &mut self.gem } pub fn peek(&self) -> i32 { self.gem.value } } ``` A: Nope\. It*would*cause problems if Valen was generating Rust MIR that it handed to rustc, but Valen's not doing that\. Valen generates LLVM IR directly[24](https://verdagon.dev/blog/boundary-memory-safety#note24)so the question is really, "Do the rustc\-generated LLVM IR and the valenc\-generated LLVM IR work together without UB?" And they do work together, because of noalias's semantics\. In C terms, if a Cmainhas two aliasing pointers, and hands one to a functionfoothat accepts it as arestrictpointer, and returns a pointer derived from that to main, main then*receives it as an aliasing pointer\.*In other words, therestrictonly applied tofoo's body\. #### Why not transpile to Rust, or Rust MIR? Transpiling to Rust wouldn't work, because Rust doesn't support things like comptime \(which I really want for e\.g\.[The Impossible Optimization](https://verdagon.dev/blog/impossible-optimization)\)\. Transpiling to Rust MIR would*almost*work, except it doesn't allow fine\-grained aliasing information\. In Rust MIR, you can specify that something isnoaliasor not\. In LLVM however, you can specify that it'snoaliasor not*or something between\.*Specifically, you can say that it aliases some things and not other things\. In group\-borrowing\-speak, you can*specify what group*it's in\. This is done via the\!alias\.scopemetadata on loads/stores, and puttingnoaliasmetadata on call expressions \(rather than just function parameters\)\. It's also slightly philosophical; while Valen is still experimental, it needs to be more decoupled from constraints, including Rust MIR\. As Valen grows and stabilizes, I could see this changing\. One possible way forward would be: - Valen stabilizes, to know what's actually useful\. - We propose adding\!alias\.scope/noaliasmetadata to Rust MIR\. - We implement and upstream those changes to rustc\. - We stop generating LLVM IR directly, and start generating MIR\. #### Why not just improve Rust? Why make another language? I think Rust would benefit from adding some of Valen's features, like linear types\. However, I'm not certain yet that we should add group borrowing to Rust directly\. Rust's entire culture, ethos, and learning curve is based on a simple principle of shared\-xor\-mutable\. It's unintuitive, but it's consistent enough that once you internalize it, it's a straightforward \(if lengthy\) path to master Rust\. Valen is all about mutable aliasing, which is the exact opposite\. Valen is also consistent, making it a straightforward path to master\. However, if you blend the two wrong, you get an inconsistent mess that theoretically has the benefits of both, but in practice also makes Rust much harder to learn\. I could see a future where Valen pivots a few times, figures out how to do group borrowing well, and armed with that strong data point we can*then*consider how to add it to Rust\. Until then, there's a lot of exploring to do\. #### Thoughts on Group Borrowing's Flexibility Q: Any more examples, besides thatfunc attackone? A: Sure, here's some more:[25](https://verdagon.dev/blog/boundary-memory-safety#note25) - **Read\-world\-mutate\-piece pattern:**If we have a function that needs to read the world but modify only a part of it, normal borrow checking often forces us to either take the entire mutable world plus IDs into it, or split our data into separate ECS\-esque tables\. Group borrowing doesn't force that because it can express that in terms of references\. - **Pure results:**In a game, if you have an NPC \(immutably\) weigh different "action possibility" structs to decide its ultimate action to execute, group borrowing lets those structs contain references, where normal borrow checking makes them store IDs\. - **RAII:**Normal borrow checking doesn't allow for RAII "rollback structs", where a e\.g\.struct Rollbackercan have a mutable reference to a e\.g\.struct Transaction, becauseRollbacker's&mut Transactionmakes theTransactionunusable to everyone else\. Ask me in[r/valen](https://reddit.com/r/valen)comments and I can expand on these if you want\. I have a feeling this is just the tip of the iceberg\. When we release this to the world, we'll be seeing a lot more patterns\. Also, there will*definitely*be an article on this soon, stay tuned\! 24 Though, Valen does give the generated LLVM IR into rustc at the very last second to sneak it into rustc's invocations of LLVM's optimization and its linking\. 25 \.\.\.plus a lot more that enable reference counting and generational references, but I don't want to go too deeply into that yet\.

Similar Articles

Safe Made Easy Pt.1: Single Ownership is (Not) Optional

Lobsters Hottest

This article introduces a new approach to memory safety based on linear types and abstract interpretation, aiming to eliminate common bugs like use-after-free and memory leaks more ergonomically than Rust.

Memory safety is a matter of life and death

Lobsters Hottest

The author argues that memory-unsafe open-source software is critically vulnerable to upcoming AI bug-finding agents, making memory safety a moral imperative, and that Rust must succeed as the leading memory-safe language with no overhead.

The Edge of Safe Rust

Lobsters Hottest

A TokioConf 2026 talk/blog post explores pushing safe Rust to its limits by implementing tracing garbage collection for complex pointer structures, sharing techniques for circular references and raw-pointer GC design.