18 ms·
Garbage collection for Rust: The finalizer frontier
- ape4 1y agoIf only there was a C++-like language with garbage collection (Java, C#, etc)
- reactordev 1y agoLatest version of C# is a fantastic choice for this. Java too but I would lean more C# due to the new delegate function pointers for source-generated p/invoke. Thing of beauty.
- reactordev 1y agoI also want to call out CppAst [0] and CppAst.CodeGen [1] projects. These two things have saved me years of my life if I were to roll these by hand. Kudos Alexandre Mutel, kudos. [0] https://github.com/xoofx/CppAst.NET https://github.com/xoofx/CppAst.NET [1] https://github.com/xoofx/CppAst.CodeGen https://github.com/xoofx/CppAst.CodeGen
- jerf 1y agoI can't be an expert in every GC implementation because there are so many of them, but many of the problems they mention are problems in those languages too. Finalizers are highly desirable to both the authors of the runtimes and the users of the languages, but are generally fundamentally flawed in GC'd languages, to the point that the advice in those languages is to stay away from them unless you really know what you are doing, and then, to stay away from them even so if you have any choice whatsoever... and that's the "solution" to these problems most languages end up going with. 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.
- king_terry 1y agoThe whole point of Rust is to not have a garbage collector while not worrying about memory leaks, though.
- GolDDranks 1y agoThe mechanisms that Rust provide for memory management are various. Having a GC as a library for usecases with shared ownership / handles-to-resources is not out of question. The problem is that they have been hard to integrate with the language.
- jvanderbot 1y agoWhile you're of course correct, there's just something that feels off. I'd love if we kept niche, focused-purpose languages once in a while, instead of having every language do everything. If you prioritize everything you prioritize nothing.
- GolDDranks 1y agoI agree specifically with regards to GC; I think that focusing on being an excellent language for low-level programming (linkable language-agnostic libraries, embedded systems, performance-sensitive systems, high-assurance systems etc.) should continue being the focus. However, this is 3rd party research. Let all flowers bloom!
- sebastianconcpt 1y ago> If you prioritize everything you prioritize nothing 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 1y agoNo, they are quite identical. Both cases logically lead to "now everything has the same priority". There's nothing about generalism in there.
- gwbas1c 1y agoBefore making criticisms that Garbage Collection "defeats the point" of Rust, it's important to consider that Rust has many other strengths: - 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.
- jvanderbot 1y agoYou've just listed "Compiled language" features. Only the 4th point has any specificity to Rust, and even then, is vague in a way that could be misinterpreted. 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.
- zamalek 1y ago- Rust is a nice language to use
- gwbas1c 1y agoOh no, I'm directly criticizing C/C++/Java/C#: 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.
- 1y ago
- torginus 1y agoWhile I'm not ideologically opposed to GC in Rust I have to note: - 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.
- taylorallred 1y agoFor those who are interested, I think that arena allocation is an underrated approach to managing lifetimes of interconnected objects that works well with borrow checking.
- worik 1y ago> works well with borrow checking. Yes, because it defeats borrow checking. Unsafe Rust, used directly, works too
- celeritascelery 1y agoIt does not defeat borrow checking. The borrow checker will ensure that objects do not outlive the arena. It works with borrow checking.
- Archit3ch 1y agoThis. Arenas don't work when you don't know when it's okay to free. The borrow checker can help with that (or you can track it manually in C/Zig).
- worik 1y agoThe borrow checker knows nothing about your arena allocations. That is if we are talking about the same thing! All the borrow checker knows is there is a chunk of memory (the arena) in scope. It works, no memory safety in the sense that you must manage your own garbage and you can reference uninitialized parts of the arena I have found myself using arenas in Rust for managing circular references (networks with cycles) and if I were to do it again I think I would write that bit in C or unsafe Rust.
- ben-schaaf 1y agoThe popular Bumpalo only returns references with the lifetime of the allocator. Not sure what you mean by manage your own garbage, an arena allocator deallocates everything when it goes out of scope. You definitely can't reference uninitialized parts of an arena.
- Dwedit 1y agoThere was one time where I actually had to use object resurrection in a finalizer. It was because the finalizer needed to acquire a lock before running destruction code. If it couldn't acquire the lock, you resurrect the object to give it a second chance to destroy (calling GC.ReRegisterForFinalize)
- nu11ptr 1y agoWhile it might be useful for exploration/academic pursuit/etc., am I the only one who finds "conservative GC" a non-starter? Even if this was fully production ready, I had a use case for it, etc. I still would never ship an app with a conservative GC. It is difficult enough to remove my own bugs and non-determinism, and I just can't imagine trying to debug a memory leak caused due to a conservative GC not finding all used memory.
- ltratt 1y agoIf you've used Chrome or Safari to read this post, you've used a program that uses (at least in parts) conservative GC. [I don't know if Firefox uses conservative GC; it wouldn't surprise me if it does.] This partly reflects shortcomings in our current compilers and in current programming language design: even Rust has some decisions (e.g. pointers can be put in `usize`s) that make it hard to do what would seem at first glance to be the right thing.
- gwbas1c 1y ago> Having acknowledged that pointers can be 'disguised' as integers, it is then inevitable that Alloy must be a conservative GC 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.
- quotemstr 1y agoWorse, conservatism in a GC further implies it can't be a moving GC, which means you can't compact, use bump pointer allocation, and so on. It keeps you permanently behind the frontier. 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.
- gwbas1c 1y agoThe thing I don't understand is why anyone would pass a pointer to a GC'ed object into a 3rd party library (that's in a different language) and expect the GC to track the pointer there? Passing memory into code that uses a different memory manager is always a case where automatic memory management shouldn't be used. IE, when I'm using a 3rd party library in a different language, I don't expect it to know enough about my language's memory model to be able to effectively clean up pointers that I pass to it.
- quotemstr 1y ago> The thing I don't understand is why anyone would pass a pointer to a GC'ed object into a 3rd party library The promise of GC is to free the programmer from the burden of memory management. If I can't give (perhaps fractional) ownership of a data structure to a library and expect its memory to be reclaimed at the appropriate time, have I freed myself from the burden of memory management?
- vsgherzi 1y agoNo has seemed to call it out yet but swift uses a form of garbage collection but remains relatively fast. I was against this at first but the more I think about it, I think it has real potential to make lots of hard problems with ownership easier to solve. I think the next big step or perhaps an alternative would be to make changes to restrictions in unsafe rust. I think the pursuit of safety is a good goal and I could see myself opting into garbage collections for certain tasks.
- worik 1y agoSwift uses reference counting Slows down every access to objects as reference counts must be maintained Something weird that I never bothered with to enable circular references
- marcianx 1y agoReference counted pointers can deference an object (via a strong pointer) without checking the reference count. The reference count is accessed only on operations like clone, destruction, and such. That being said, access via a weak pointer does require a reference count check.
- fulafel 1y agoThis sounds different from common refcounting semantics in other languages, is it really so in Swift? Usually access increases the reference count (to avoid the object getting GC'd while you use it) and weak pointers are the exception where you are prepared for the object reference to suddenly become invalid.
- worik 1y agoI have thought for years Rust needs to bifurcate. Asyc/await really desperately needs a garbage collector. (See this talk from Rustconf 2025: https://youtu.be/zrv5Cy1R7r4?si=lfTGLdJOGw81bvpu https://youtu.be/zrv5Cy1R7r4?si=lfTGLdJOGw81bvpu and this blog:https://rfd.shared.oxide.computer/rfd/400 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 https://doc.rust-lang.org/std/pin/index.html] that are due to the lack of a garbage collector What a good idea
- sunshowers 1y agoHi -- I'm the one who presented the talk -- honored! I'm curious how you got to "async Rust needs a [tracing] garbage collector" in particular. While it's true that a lot of the issues here are downstream of futures being passive (which in turn is downstream of wanting async to work on embedded), I'm not sure active futures need a tracing GC. Seems to me like Arc or even a borrow-based approach would work, as long as you can guarantee that the future is dropped before the scope exits (which admittedly isn't possible in safe Rust today [0]). [0]: https://without.boats/blog/the-scoped-task-trilemma/ https://without.boats/blog/the-scoped-task-trilemma/
- worik 1y agoMy comment is quite general. The difficulties with async/await seem to me to be with the fact that code execution starts and stops using "mysterious magic", and it is very hard for the compiler to know what is in, and what is out, of scope. I am by no means an expert on async/await, but I have programmed asynchronously for decades. I tried using async/await in Rust, Typescript and Dart. In Typescript and Dart I just forget about memory and I pretend I am programming synchronously. Managed memory, runtimes, money in the bank, who is complaining? Not me. \digression{start} This is where the first problem I had with async/await cropped up. I do not like things that are one thing, and pretend to be another - personally or professionally - and async/await is all about (it seems to me) making asynchronous programming look synchronous. Not only do I not get the point - why? is asynchronous programming hard? - but I find it offensive. That is a personal quibble and not one I expect many others to find convincing I guess I am complaining.... \digression{end} In Rust I swiftly found myself jumping through hoops, and having to add lots and lots of "magic incantations" none of which I needed in the other languages. It has been a while, and I have blotted out the details. Having to keep a piece of memory in scope when the scope itself is not in my control made me dizzy. I have not gone back and used async/await but I have done a lot of asynchronous rust programming since, and I will be doing more. My push for Rust to bifurcate and become two languages is because async/await has sucked up all the oxygen. Definitely from asynchronous Rust programming, but it has wrecked the culture generally. The first thing I do when I evaluate a new crate is to look for "tokio" in the dependencies - and two out of three times I find it. People are using async/await by default. That is OK, for another language. But Rust, as it stands, is the wrong choice for most of those things. I am using it for real time audio processing and it is the right choice for that. But (e.g) for the IoT lighting controller [tapo](https://github.com/mihai-dinculescu/tapo https://github.com/mihai-dinculescu/tapo) it really is not. I am resigned to my Cassandra role here. People like your good self (much respect for your fascinating talk, much envy for your exciting job) are going to keep trying to make it work. I think it will fail. It is too hard to manage memory like Rust does with a borrow checker with a runtime that inserts and runs code outside the programmer's control. There is a conflict there, and a lot of water is going under the bridge and money down the drain before people agree with me and do what I say... Either that or I will be proved wrong Lastly I have to head off one of the most common, and disturbing, counter (non) arguments: I absolutely do not accept that "so many smart people are using it it must be OK". Many smart people do all sorts of crazy things. I am old enough to have done some really crazy things that I do not like to recall, and anyway, explain Windows - smart people doing stupid things if ever
- fithisux 1y agoVery important paper.
- sebastianconcpt 1y agoI'm curious about the applicability. If memory management is already resolved with the borrow checker rules, then what case can make you want a GC in a Rust program?
- trueismywork 1y agoLock free programming..
- dajonker 1y agoImplementing a doubly linked list without either unsafe or some very confusing code that could arguably win an obfuscation contest.
- antonvs 1y ago> If memory management is already resolved with the borrow checker rules Even in standard Rust, this only applies to a subset of memory management. That’s why Rust supports reference counting, for example, which is an alternative to borrow checking. But one could make the case that automatic garbage collection was developed specifically to overcome the problems with reference counting. Given that context, GC in Rust makes perfect sense.
- FridgeSeal 1y agoI don’t understand the desire to staple a GC into Rust. If you want this, you might just…want a different language? Which is fine and good! Putting a GC on Rust feels like putting 4WD tyres on a Ferrari sports car and towing a caravan with it. You could (maybe) but it feels like using the wrong tool for the job.
- dajonker 1y agoIf I understand the article correctly it's for those cases where you want memory safety (i.e. not using "unsafe") but where the borrow checker is really hard to work with such as a doubly linked list, where nodes can point to each other. For the rest you'd still use non-GC rust.
- FridgeSeal 1y agoI just foresee it become irrevocably viral, as it becomes the “meh, easier” option, and then suddenly half your crates depend on it, and then you’re losing one of the major advantages of the language.
- zozbot234 1y agoA doubly linked list is not the optimal case for GC. It can be implemented with some unsafe code, and there are approaches that implement it safely with GhostCell (or similar facilities, e.g. QCell) plus some zero-overhead (mostly) "compile time reference counting" to cope with the invariants involved in having multiple references simultaneously "own" the data. See e.g. https://github.com/matthieu-m/ghost-collections https://github.com/matthieu-m/ghost-collections for details. Where GC becomes necessary is the case where even static analysis cannot really mitigate the issue of having multiple, possibly cyclical references to the same data. This is actually quite common in some problem domains, but it's not quite as simple as linked lists.
- lmm 1y agoAdding a GC to Rust might honestly be easier than getting the OCaml ecosystem to adopt something that works as well as cargo. It's tragic, but that's the world we live in.
- rurban 1y agoMemory safety would be a good idea indeed. Just to get rid of the unsafeties in the stdlib and elsewhere. With this would also go type-safety, because there will be no more unsafe hacks. Concurrency safety would another hill to die on, as they choose not to approach this goal with their blocking IO and locks all over.