Which does at least generally work. It's pretty rare to be bitten by these problems, like, less-than-once-per-career levels of rare (if you honor the advice above)... but certainly not unheard of, definitely not a zero base rate.
- Rust has no overhead from a "framework"
- Rust programs start up quickly
- The rust ecosystem makes it very easy to compile a command-line tool without lots of fluff
- The strict nature of the language helps guide the programmer to write bug-free code.
In short: There's a lot of good reasons to choose Rust that have little to do with the presence or absence of a garbage collector.
I think having a working garbage collection at the application layer is very useful; even if it, at a minimum, makes Rust easier to learn. I do worry about 3rd party libraries using garbage collectors, because they (garbage collectors) tend to impose a lot of requirements, which is why a garbage collector usually is tightly integrated into the language.
Rust's predominant feature, the one that brings most of its safety and runtime guarantees, is borrow checking. There are things I love about Rust besides that, but the safety from borrow checking (and everything the borrow checker makes me do) is why I like programming in rust. Now, when I program elsewhere, I'm constantly checking ownership "in my head", which I think is a good thing.
The heavyweight framework (and startup cost) that comes with Java and C# makes them challenging for widely-adopted lightweight command-line tools. (Although I love C# as a language, I find the Rust toolchain much simpler and easier to work with than modern dotnet.)
Building C (and C++) is often a nightmare.
Do you mean the VM/runtime? If so, you might be able to eliminate that with an AOT build.
> I find the Rust toolchain much simpler and easier to work with than modern dotnet
What part of the toolchain? I find them pretty similar with the only difference being the way you install them (with dotnet coming from a distro package and Rust from rustup)
1) dynamic loading of jar files
2) reflection
Number 1 allows you to load arbitrary jar files with code and execute them. Number 2 allows you to programmatically introspect existing code and then execute logic like "Find me all Foo sub classes and create an instance of those and return the list of those objects". You can do that at any time but a lot of that kind of stuff happens at startup. That involves parsing, loading and introspecting thousands of class files in jar files that need to be opened and decompressed.
Most of "Java is slow" is basically programs loading a lot of stuff at startup, and then using reflection to look for code to execute. You don't have to do those things. But a lot of popular web frameworks like Spring do. A lot of that stuff is actually remarkably quick considering what it is doing. You'd struggle to do this in many other languages. Or at all because many languages don't have reflection. If you profile it, there are millions of calls happening in the first couple of seconds. It's taking time yes. But that code has also been heavily optimized over the years. Dismissing what that does as "java is slow" and X is fast is usually a bit of an apples and oranges discussion.
With Spring Boot, there are dozens of libraries that self initialize if you simply add the dependency or the right configuration to your project. We can argue about whether that's nice or not; I'm leaning to no. But it's a neat feature. I'm more into lighter weight frameworks these days. Ktor server is pretty nice, for example. It starts pretty quickly because it doesn't do a whole lot on startup.
Loading a tiny garbage collector library on startup isn't a big deal. It will add a few microseconds to your startup time maybe. Probably not milliseconds. Kotlin has a nice native compiler. If you compile hello world with it it's a few hundred kilobytes for a self contained binary with the program, runtime, and the garbage collection. It's not a great garbage collector. For memory intensive stuff you are better off using the JVM. But if that's not a concern, it will do the job.
Java starting slowly is mostly from all the cruft in the typical java app, with springboot, dependency injection frameworks, registries etc. You don't have to have those, it's just that most java devs use them and can't conceive of a world of low dependencies
Still not great for commandline apps, but java itself is much better than java devs
Java's major competitors, C# and Go, both have had value types since day one and reified generics since they gained generics; this hasn't posed any major problems to either language (with the former being IMO already more complex than Java, but the latter being similarly or even less complex than Java).
If the technical side isn't that hard, I'd have expected the JVM to have implemented value types already, making it available to other less conservative languages like Kotlin, while work on smoothly integrating it in Java took as long as needed. Project Valhalla is over a decade old, and it still hasn't delivered, or even seems close to delivering, its primary goals yet.
Just to be clear, I don't think every language needs to meet every need. The lack of value types is not a critical flaw of Java in general, as it only really matters when trying to use Java for certain purposes. After all, C# is very well suited to this niche; Java doesn't have to fit in it too.
Yes, but at a significant cost to FFI and/or GC and/or user-mode threads (due to pointers into the stack and/or middle of objects). Java would not have implemented value types in this way, and doing it the way we want to would have been equally tricky had it been done in Java 1.0. Reified generics also come at a high price, that of baking the language's variance strategy into the ABI (or VM, if you want). However, value types will be invariant (or possibly extensible in some different way), so it would be possible to specialise generics for them without necessarily baking the Java language's variance model into the JVM.
> If the technical side isn't that hard, I'd have expected the JVM to have implemented value types already, making it available to other less conservative languages like Kotlin, while work on smoothly integrating it in Java took as long as needed
First, that's not how we do things. Users of all alternative Java Platform languages (aka alternative JVM languages) combined make up less than 10% of all Java platform users. We work on the language, VM, and standard library all together (this isn't the approach taken by .NET, BTW). We did deliver invokedynamic before it was used by the Java language, but 1. that was after we knew how the language would use it, and 2. that was at a time when the JDK's release model was much less flexible.
Second, even if we wanted to work in this way, it wouldn't have mattered here. Other Java Platform languages don't just use the JVM. They make extensive use of the standard library and observability tooling. Until those are modified to account for value types, just a JVM change would be of little use to those languages. The JVM comprises maybe 25% of the JDK, while Kotlin, for example, makes use of over 95% of the JDK.
In contrast, "time" reports that rust takes 1ms, which is the limit of it's precision.
Python does Hello World in just 8ms, despite not having a separate AOT compilation step.
The general guidance I've seen for interaction is that things start to feel laggy at 100ms; so 30ms isn't a dealbreaker, but throwing a third of your time budget at the baseline runtime cost is a pretty steep ask.
If you want to use the application as a short lived component in a larger system, than 30ms on every invocation can be a massive cost.
If you like rust, use rust. It's very likely the best rust
This isn't even a new concept in Rust. Rust already has a well accepted RC<T> type for reference counted pointers. From a usage perspective, GC<T> seems to fit in the same pattern.
Add opt-in development compilation JIT for quick iteration and you don't need any other language. (Except for user scripts where needed.)
I don't know what you had to go through before reaching rust's secure haven, but what you just said is true for the vast majority of compiled languages, which are legions.
- the syntax is hella ugly
- GC needs some compiler machinery, like precise GC root tracking with stack maps, space for tracking visted objects, type infos, read/write barriers etc. I don't know how would you retrofit this into Rust without doing heavy duty brain surgery on the compiler. You can do conservative GC without that, but that's kinda lame.
Yes, because it defeats borrow checking.
Unsafe Rust, used directly, works too
C# / dotnet don't have this issue. The few times I've needed a raw pointer to an object, first I had to pin it, and then I had to make sure that I kept a live reference to the object while native code had its pointer. This is "easier done than said" because most of the time it's passing strings to native APIs, where the memory isn't retained outside of the function call, and there is always a live reference to the string on the stack.
That being said, because GC (in this implementation) is opt-in, I probably wouldn't mix GC and pointers. It's probably easier to drop the requirement to get a pointer to a GC<T> instead of trying to work around such a narrow use case.
I remain bitterly disappointed that so much of the industry is so ignorant of the advances of the past 20 years. It's like it's 1950 and people are still debating whether their cloth and wood airplanes should be biplanes or triplanes.
I think the pursuit of safety is a good goal and I could see myself opting into garbage collections for certain tasks.
Slows down every access to objects as reference counts must be maintained
Something weird that I never bothered with to enable circular references
Asyc/await really desperately needs a garbage collector. (See this talk from Rustconf 2025: https://youtu.be/zrv5Cy1R7r4?si=lfTGLdJOGw81bvpu and this blog:https://rfd.shared.oxide.computer/rfd/400)
Rust that uses standard techniques for asynchronous code, or is synchronous, does not. Async/await sucks all the oxygen from asynchronous Rust
Async/await Rust is a different language, probably more popular, and worth pursuing (for somebody, not me) it already has a runtime and the dreadful hacks like (pin)[https://doc.rust-lang.org/std/pin/index.html] that are due to the lack of a garbage collector
What a good idea
king_terry•6h ago
GolDDranks•6h ago
jvanderbot•5h ago
GolDDranks•5h ago
However, this is 3rd party research. Let all flowers bloom!
sebastianconcpt•5h ago
Well...
If you prioritize everything you prioritize generalism.
(the "nothing" part comes from our natural limitation to pay enough multidisciplinary attention to details but that psychological impression is being nuked with AI as we speak and the efforts to get to AGI are an attempt to make synthetic "intelligence" be able to gain dominion over this spirit before us)
virgilp•4h ago
bryanlarsen•3h ago
IainIreland•4h ago
For a variety of reasons I don't think this particular approach is a good fit for a JS engine, but it's still very good to see people chipping away at the design space.
quotemstr•2h ago
zorgmonkey•1h ago
quotemstr•1h ago
If, OTOH, Alloy is handle based, then maybe there's hope. Still a weird choice to use Rust this way.
ltratt•53m ago
I don't think Rust's design in this regard is ideal, but then again what language is perfect? I designed languages for a long while and made far more, and much more egregious, mistakes! FWIW, I have written up my general thoughts on static integer types, because it's a surprisingly twisty subject for new languages https://tratt.net/laurie/blog/2021/static_integer_types.html
hedora•3h ago
(It's safe to leak a promise, so there's no way for the borrow checker to prove an async function actually returned before control flow is handed back to the caller.)
dzaima•3h ago
Manishearth•3h ago
It's useful to have when you have complex graph structures. Or when implementing language runtimes. I've written a bit about these types of use cases in https://manishearth.github.io/blog/2021/04/05/a-tour-of-safe...
And there's a huge benefit in being able to narrowly use a GC. GCs can be useful in gamedev, but it's a terrible tradeoff to need to use a GC'd language to get them, because then everything is GCd. library-level GC lets you GC the handful of things that need to be GCd, while the bulk of your program uses normal, efficient memory management.
zorgmonkey•1h ago
James_K•3h ago
https://doc.rust-lang.org/std/boxed/struct.Box.html#method.l...
gizmo686•2h ago
The fact that any felt it nessasary to add a "leak" function to the standard library should tell you something about how easy it is to accidentally leak memory.
umanwizard•1h ago
Rust has loads of other advantages over C++, though.