Port React Compiler to Rust
Posted by boudra 7 hours ago
Comments
Comment by arcadialeak 4 hours ago
I support wholeheartedly the move to AOT-compiled languages but it looks like paying off the cognitive debt is going to be brutal on whichever team gets to maintain it in the long run.
Comment by herrkanin 4 hours ago
Comment by arcadialeak 3 hours ago
Comment by dwb 4 hours ago
Comment by bwfan123 2 hours ago
Sure, but what machine model do the public contract docs and RFCs target ? While the specific coding language is a detail, programming languages target a precise machine model. There is no machine model for english prompts and spec docs. So expect fuzziness - he-said-she-said bugs in your code - things that the LLM made up because the RFCs were not precise. And by the time you add precision to the specs, it has morphed into a new programming language with a machine model. In this usecase, since it is a port, the impact is mitigated because it is a clone of some existing functionality.
Comment by xedrac 3 hours ago
Comment by absintini 3 hours ago
Comment by phillmv 2 hours ago
Comment by rowanG077 2 hours ago
Of course that doesn't mean that there are no businesses logic bugs.
Comment by rvz 2 hours ago
Just creating permanent cognitive debt for everyone else with no-one else understanding what the code is doing.
Comment by lioeters 2 hours ago
Comment by bwfan123 1 hour ago
Comment by game_the0ry 1 hour ago
Comment by molf 5 hours ago
Very curious to see how these rewrites play out. Is the LLM foundation solid enough to build upon and iterate on? Or does this cause projects to become unmaintainable because no person understands the implementation anymore?
Comment by IshKebab 4 hours ago
Comment by benjaminleonard 3 hours ago
Had it convert it in this and the stock version, compare the output, fix and repeat.
Comment by absintini 4 hours ago
Comment by pjmlp 4 hours ago
> This is an experimental, work-in-progress port of React Compiler to Rust. ...
And then gets merged.
So after the craziness to use node on the backend, we are back to using compiled languages to compile Javascript assets and Web resources, just like Java and .NET were doing in the 2000's, however since it is Go and Rust it is cool, not the boring languages grandpas were using on their heyday.
Comment by CamouflagedKiwi 4 hours ago
I'm not sad to see the pendulum swing away from "javascript everywhere" though.
Comment by pjmlp 4 hours ago
Likewise, .NET has had NGEN since day one even if with limitations, and after several detours in AOT approaches, NativeAOT is in the package and cross platform, thus also a moot point in 2026.
But again, they aren't cool for the kids today.
Comment by insanitybit 4 hours ago
And yes, both languages are considered "not cool" since they lean heavily into code structuring that the industry has largely moved past.
Comment by gavinray 1 hour ago
AOT .NET is an official first party distribution target that in my experience is on par with building a Go binary
Don't knock it till you try it
Comment by pjmlp 3 hours ago
Comment by insanitybit 2 hours ago
I don't see what Python has to do with anything.
Comment by pjmlp 45 minutes ago
In fact both languages conveniently map to COM object model, and Microsoft is putting that to good effect.
Than we have Swift and Kotlin, plenty of OOP on those Apple and Google frameworks.
Zig, Odin, remain to be seen how industry relevant they will be, and yet examples of structs with function pointers abound among their projects, C OOP style.
So....
Comment by CamouflagedKiwi 4 hours ago
And I do still think there is an advantage for this kind of tooling to choose languages that are designed to compile to a native binary. At best Java and C# are equivalent to Go and Rust here, maybe they are less "cool" but I don't see any reason why this rewrite would be better if it were to Java than Rust.
Comment by Aurornis 3 hours ago
> And then gets merged.
The initial commit attached to that message was the start of the experiment. Are you reading the first message as if it was the last?
It was merged after 3 months and a huge number of incremental commits. The code that was merged was completely different than the code used to open the experimental WIP PR
Why is it a criticism that they started as a work in progress experiment? That’s how experiments work. It’s normal to push code as a work in progress PR and then add to it over time.
Comment by jchw 4 hours ago
I suspect this won't have as big of a shit storm as the Bun port in part just due to the input/output nature of the React compiler.
That said, while I use React still, I still have never tried the React compiler... So I have no idea how important this is. But you know, very few people are ever upset over faster iteration cycles or CI builds.
Comment by JCTheDenthog 2 hours ago
Only if the existing tests were actually useful in what they tested and covered.
Comment by jchw 2 hours ago
It is possible to have thousands of tests and a lot of blind spots, but it's easier to get a grip on that using instrumentation like code coverage and indeed by using LLMs to prod at edge cases.
I am OK with trusting that it is likely the React team understands how to rigorously test based on how well-tested React itself is.
Comment by olalonde 4 hours ago
Comment by gilgamesh-OG 3 hours ago
Comment by absintini 2 hours ago
The AI can iterate faster in Python too, guaranteed, because you don’t have any compile time.
Also, this is how AIs already get stuff done: they write SCRIPTS to perform tasks in a sandbox with tool calls. This is the future, dynamic languages, not using static languages.
Comment by Surac 6 hours ago
Comment by giancarlostoro 5 hours ago
Whats funny is I had been using Rust more with Claude because of this, and before Anthropic did their rewrite of Bun I tried a Rust rewrite of a C# project I had laying around (.NET 3.5 from back in 2008) it wasnt perfect but its nearly there now, mostly for fun, and I did it because for a little while I realized LLMs can be useful for more serious refactors, and figured it might be good for a language rewrite. Sure enough.
I think rewriting tooling that takes text and transforms it in a language like Rust is fine for JS projects, so is Go, which is why TypeScript is migrating to Go. The “free” optimized speed increases are worth it, at the temporary cost of dealing with the migration for a year or two, which with LLMs trims down the initial work into a week effort it seems? Wild.
Comment by bogdanoff_2 2 hours ago
I have noticed, in general, LLMs tend to "fix" problems by shoving them under the rug (like adding a cast) or writing a super-local "fix" instead of taking the time to understand the deeper problem or structure.
In Rust, when you get stuck in a complicated borrow-checking problem, Rust people will tell you it's a Good Thing(tm) because it forces you to think about the higher level architecture of your code. An LLM, on the other hand, might bash its head a couple of times trying to "fix" the problem, and then just throw in a Refcell (or other workaround), see that it compiles, and call it a day.
Refcells "move borrow checking to runtime", meaning that the code will compile, and will crash at runtime if the object is tried to be accessed from two different places at the same time. In most "normal" programming languages it's not an issue -- but it's a crash in Rust.
Now, maybe the models have gotten better, and maybe you can get around this problem by using a good system prompt/"tools" and a good testing methodology. What I am saying however is that you shouldn't automatically take "rewritten to Rust by AI" at face value of it being good Rust code, or a testimonial of "Rust and AI being a good match". (Go is better I think)
Comment by nirui 5 hours ago
Memory safety is just a tiny part of over all security. If a LLM can transcode correctly, then it should also output 100% correct C code.
On the other hand, If a LLM cannot correctly transcode, then using Rust may just make the bug soundless, because the language runtime/code-gen "avoided" usual punishments that might make the bug (and bug report) obvious.
Comment by tialaramex 25 minutes ago
Sure, in the same way that foundations are just a tiny part of house construction.
Foundations come first, it's great if you've got more on top, I recommend it actually, but if you don't have the foundations it's not even worth having the discussion about what else you built, it's all worthless.
Comment by insanitybit 4 hours ago
No, it's a pretty massive part with disproportionate severity.
> If a LLM can transcode correctly, then it should also output 100% correct C code.
Translating code seems to largely rely on having a strong suite of existing tests, not on ability to code correctly.
It's unclear if LLMs are great at writing safe C code, it's much clearer that they can meet targets with external feedback properties like "test passes/fails".
> On the other hand, If a LLM cannot correctly transcode, then using Rust may just make the bug soundless, because the language runtime/code-gen "avoided" usual punishments that might make the bug (and bug report) obvious.
This is very unclear to me.
Comment by kouteiheika 4 hours ago
70%[1][2] is tiny?
[1]: https://www.zdnet.com/article/microsoft-70-percent-of-all-se... [2]: https://www.chromium.org/Home/chromium-security/memory-safet...
> On the other hand, If a LLM cannot correctly transcode, then using Rust may just make the bug soundless, because the language runtime/code-gen "avoided" usual punishments that might make the bug (and bug report) obvious.
Isn't it the other way around? Rust guarantees lack of undefined behavior in safe code. If you have undefined behavior in your code your bug might become a heisenbug, or make the rest of your program behave weird, or the bug might simply be dormant until a very specific situation occurs (i.e. be "soundless" as you say).
If you're going to automatically translate your code from one language to another then a memory-safe target language (whether it's Rust, Java, C# or something else) is the only sane, reasonable choice. And if you want C or C++-like performance (i.e. you want to maximize performance) then you're pretty much left with Rust on the table.
Comment by norman784 4 hours ago
So while LLMs are good at writing walls of code, they do not produce good code, just good enough and sometimes it is wrong (here is where Rust can help a bit by checking that the program is sound, but for the most part you should also validate the logic).
The dream language for LLMs would be one that has some kind of proving that function inputs/outputs are what you expect (I think it's called proof theory, but it's not my area of expertise, so I could be wrong), you kind of can emulate this with new types[0].
[0] https://doc.rust-lang.org/rust-by-example/generics/new_types...
Comment by swiftcoder 4 hours ago
An LLM can't (currently) transcode correctly in a vacuum. It needs tight guardrails to keep it on the straight-and-narrow (such as an existing conformance test suite that is extremely comprehensive).
The value of transcoding to Rust specifically is that the compiler gives you a pretty substantial set of guardrails "for free" - in a C port, your conformance test suite would also need to test every aspect of memory safety and fearless concurrency...
Comment by IshKebab 3 hours ago
Well LLM's cannot transcode perfectly correctly, so the fact that Rust has lots of static checking is really important. Not just for memory safety - Rust helps with many other classes of bugs too.
> then using Rust may just make the bug soundless, because the language runtime/code-gen "avoided" usual punishments that might make the bug (and bug report) obvious.
I think what you're saying here is that LLM's often cheat to solve the immediate error, e.g. by using `unsafe` where you really shouldn't, or just making a test not test anything. That's definitely possible.
Comment by EddieRingle 3 hours ago
I get the opposite impression. If they aren't confident enough to write it by hand, I think it means the language is either hard to write, hard to read, or both. And by delegating to a LLM, there's no "barrier of entry" being lifted, it's just saving you typing time (like it would if it was being translated to C or Kotlin). If they actually decide to "focus on all the other programming logic" and they aren't just vibe coding this, they'll still need to be able to understand and reason about the code.
Comment by fg137 5 hours ago
Does that actually happen?
Comment by insanitybit 4 hours ago
Comment by EddieRingle 3 hours ago
Comment by insanitybit 3 hours ago
Comment by EddieRingle 9 minutes ago
> the borrow checker woes can be offloaded to the model, you focus on all the other programming logic
Then the person you responded to asked if "you" really do "focus on all the other programming logic", given that "the model" deals with the borrow checker. In other words, they're asking about the work the actual human is doing at that point. You then replied talking about the model again and token budgets. In effect, you brushed the question under the rug and imply that _everything_ is offloaded to the model, no focus by the human required.
Comment by rob74 5 hours ago
I think you actually meant "lower the barrier" (which would make it easier to pass the barrier, while lifting it makes it harder)?
Comment by swiftcoder 4 hours ago
Comment by cjbgkagh 2 hours ago
Comment by incognito124 5 hours ago
Comment by pjmlp 4 hours ago
Also I think this is all temporary, just like devs still did not believe on optimising compilers and checked each line of generated Assembly code, during the 1960's.
Eventually it will be natural language, and all languages will be kind of irrelevant.
Comment by lioeters 3 hours ago
Comment by KolmogorovComp 2 hours ago
Comment by cindyllm 5 hours ago
Comment by willsmith72 6 hours ago
Haven't heard about since ages ago when it was extremely slow
Comment by molf 6 hours ago
It's brilliant: all useMemo and useCallback can be removed and you get the same runtime performance and then some, at the cost of only a slight increase in code size.
A small downside at the moment is the build time. This change will hopefully help address that because it will no longer depend on babel.
Comment by DanielHB 4 hours ago
I would be interest to hear how it worked out for you.
Comment by molf 4 hours ago
- Lints [1] that flag code that cannot be (correctly) optimised. Usually this is obscure code that is too smart for its own good. But the compiler leaves it alone and flags it for review, so most things just keep working.
- Lints that flag code that violate the rules of hooks. These rules became more critical to follow: failure to do so may break rendering. But non-compliant code can be easily be excluded from compilation [2], so you do not have to fix everything at once.
- Popular libraries that are not compatible (yet) are flagged and excluded automatically [3].
The compiler is better than manual memoization, because 1) it is hard not to forget memoizations, and 2) the compiler's output memoizes more granularly than manual memoization realistically could.
I have not found performance regressions. Not saying they're not possible; but we haven't encountered them.
We have a very performance-sensitive project that used preact (chosen for performance) via its compatibility layer, that we switched to React + React compiler. Performance is noticeably better than with preact. Whereas previously the React-only version was incredibly slow even with carefully placed memoizations, because they were very hard to get right.
[1]: https://react.dev/learn/react-compiler/installation#eslint-i...
[2]: https://react.dev/learn/react-compiler/incremental-adoption
[3]: https://react.dev/reference/eslint-plugin-react-hooks/lints/...
Comment by DanielHB 1 hour ago
const nestedDependency = { a: { b: { c: 'c' } } }
useMemo(() => nestedDependency.a.b.c, [nestedDependency])
vs
useMemo(() => nestedDependency.a.b.c, [nestedDependency.a.b.c])
neither triggers react hook lint warnings, although I guess this is more relevant to useEffect than memoization.
Comment by molf 15 minutes ago
Comment by _the_inflator 5 hours ago
Comment by esperent 5 hours ago
I also took a step to clean out all memo, useMemo, useCallback across the app and so far have not run into any issues from doing this.
Comment by veidelis 4 hours ago
Comment by paavohtl 6 hours ago
Comment by Ambroos 4 hours ago
Comment by pjmlp 4 hours ago
Not sure how they relate to each other.
Comment by ameliaquining 4 hours ago
Comment by mohsen1 5 hours ago
Comment by bingemaker 4 hours ago
Comment by theappsecguy 4 hours ago
Comment by bingemaker 4 hours ago
Comment by insanitybit 4 hours ago
Comment by ramon156 6 hours ago
Seems like they kind of did that? The thread seems like people already were waiting on this, so that's positive.
Comment by odie5533 2 hours ago
Comment by dust-jacket 2 hours ago
Comment by Trung0246 7 hours ago
Comment by jon-wood 6 hours ago
Comment by koito17 6 hours ago
Just cause it isn't used for webshit doesn't mean "approximately no one" has heard of it.
Lean is pretty much the most popular language mathematicians use today for computer-assisted proofs. More mature audiences may know about Rocq, Isabelle, etc., but Lean was already popular enough for a few people I know to have written their PhD theses on it about a decade ago.
I think GP is joking about a port to Lean because that would at least produce a formally verifiable output.
Comment by HeavyStorm 6 hours ago
I like Lean (and more generally dependent types) but ffs Lean has a very, very small userbase for a project like this. GGP would have to really justifyv the benefits for such a switch.
Comment by bhy 6 hours ago
Comment by giancarlostoro 5 hours ago
My first programming interview my interviewer was like what the heck are you doing with D? And he noted he has a room full of devs where nobody knows what D is.
Comment by ryanshrott 2 hours ago
Comment by bhouston 5 hours ago
Comment by awesan 5 hours ago
Comment by pmarin 3 hours ago
Comment by logicprog 5 hours ago
Comment by voidUpdate 6 hours ago
Comment by LoganDark 6 hours ago
Comment by iammrpayments 6 hours ago
Comment by __jonas 6 hours ago
You may be thinking about React itself, not the new compiler? I'm sure there must be some flow in there still from back in the day.
Comment by LoganDark 6 hours ago
I just checked out Flow and woah. First-class syntax sugar for React. Maybe cool. (If it doesn't break catastrophically in a sane build system like Vite)
Comment by voidUpdate 6 hours ago
Comment by bandrami 6 hours ago
Comment by LoganDark 6 hours ago
TypeScript is memory-safe, but you can't really control where the memory comes from. In Rust you have structs, traits, references and all sorts of things to control both your memory usage and your memory efficiency, and you just don't really get that in TypeScript. Plus, in Rust it's a lot easier to utilize multithreading -- JS is notoriously tedious to parallelize (message-passing between JS workers is a bit annoying compared to structured concurrency in Rust)
Comment by epolanski 5 hours ago
Comment by ai_slop_hater 2 hours ago
Comment by LoganDark 6 hours ago
It seems backwards that they are freezing the Babel AST into the interoperability contract and only using the more efficient native representations in an isolated fashion -- shouldn't it be the other way around?
Comment by molf 6 hours ago
> Note that the conversion from any AST into our HIR is complex, and we can only maintain one version. Hence we've aligned on using a Babel-like AST as our public API. Another key point is that we don't yet implement our own scope analysis (since the TS version of the compiler relied on Babel's scope analysis), so for now we require that the scope data be serialized. It's a denormalized graph, and some metadata has to be stored to associate nodes with scopes. We're open to feedback about the AST and scope representation - we iterated a bit just to get things to work, but it can be more optimal.
Comment by LoganDark 6 hours ago
Comment by molf 6 hours ago
The current developers surely are more familiar with the Babel representation than OXC, so why switch?
Comment by LoganDark 6 hours ago
Comment by swiftcoder 4 hours ago
This is just how software development for a large public project goes. If you want to land big changes in a finite amount of time, you take the path that breaks the least number of things along the way.
Once the whole ecosystem is rust-based, they can always trim out some of the redundant intermediate representations for performance gains.
Comment by AbuAssar 6 hours ago
Comment by flufluflufluffy 5 hours ago
Comment by swiftcoder 4 hours ago
Comment by AbuAssar 5 hours ago