5 ms·
Excuse my ignorance (I have only just started learning Rust), but why is GC by default desirable? If a programming language can tell when a variable goes out of
by dcuthbertson 3y ago
Excuse my ignorance (I have only just started learning Rust), but why is GC by default desirable? If a programming language can tell when a variable goes out of scope, or its lifetime ends and use that information to automatically release allocated memory, then why is having a garbage collector important? Does Rust (as a result of not having GC) put restrictions on the kind of code you can write, or force one to write code in such a way that it's difficult to reason about?
- nu11ptr 3y agoYes, it has a borrow checker which restricts some valid code and occasionally makes you jump through hoops and write in a different style. Also, a garbage collector that is state of the art (bump allocation, generational, and compacting) is faster overall typically (throughput-wise, but with less predictable latency) than naïve Rc/Arc all over the place (due to usage of "free list" allocator and counter bumps). That isn't to say blisteringly fast Rust isn't possible to write, and in fact it is pretty easy to write by avoiding Rc/Arc except where necessary and using the stack with the borrow checker, but this entails a writing style that is more "low level" and thus takes a bit more thinking.
- dcuthbertson 3y agoThanks for that explanation! I'm going to have to dive into Rc/Arc [0]. Reference counting built into the standard library is really interesting. Several years ago I wrote a Windows kernel minifilter and reference counting is used a lot by anything that interacts with the filter manager. RC was very helpful to ensure the minifilter didn't leak memory when it was unloaded. [0]: https://doc.rust-lang.org/std/sync/struct.Arc.html https://doc.rust-lang.org/std/sync/struct.Arc.html
- HankB99 3y agoIANAGCE (I am not a GC expert.) I thought there were potential costs to using GS. 1. Interrupting the program to sweep, resulting in unpredictable performance. 2. Possibility that a circularly linked group of variables could be impossible to sweep, resulting in memory leaks. 3. Need to check reference counts (along with bounds checks) that degrade performance. Lack of GC (and bounds checking) are factors that make C/C++ performant and then lead to the kind of bugs that result in programs that don't do what they're supposed to do (and at worst, result in security vulnerabilities.) I thought a key goal of Rust was to fix these problems without sacrificing performance.
- nu11ptr 3y ago> 1. Interrupting the program to sweep, resulting in unpredictable performance. See my comment regarding "less predictable latency" (although new advances are making for much more predictable latency - see some of the work done in Java GCs for example) > 2. Possibility that a circularly linked group of variables could be impossible to sweep, resulting in memory leaks. That is not possible in a precise tracing collector, only in reference counting (Rc/Arc) > 3. Need to check reference counts (along with bounds checks) that degrade performance. I think you are referring to reference counting again. Both Rust and C++ have reference counting types as an add on. State of the art tracing collectors don't collect/check dead objects, they compact live ones, and often don't use reference counting directly. Most objects die young. > Lack of GC (and bounds checking) are factors that make C/C++ performant and then lead to the kind of bugs that result in programs that don't do what they're supposed to do (and at worst, result in security vulnerabilities.) Replace "performant" with "predictable performance" > I thought a key goal of Rust was to fix these problems without sacrificing performance. Rust, like C/C++, wants you to be able to predict performance and latency and without the baggage of a runtime. Nothing more than trade offs - neither is better or worse. GCs often do perform better with the trade off of latency predictability, but as always it depends on use case as to what is appropriate. Rust likely made the right choice for its domain.
- kaba0 3y agoWhat kind of GC do you talk about here? (RC is also a GC algorithm, but you seem to mix some of its shortcomings with that of trading GCs). 1) this is generally true, but many part of this can be done concurrently, and if we want to improve latency at the cost of some throughput than there are low-lat GCs that maximize the pause times (it is basically independent from the heap size), so in practice you have similar interrupts as you would from the OS alone. 2) this is not a problem with tracing GCs, only with refcounting (most well known is Python, ObjC and Swift for this perhaps). You can still leak memory everywhere by e.g. storing them in a huge list forever, though, but that is a much rarer and easy to debug bug. 3) this is again only true for RC, and it is the reason why it is slower than tracing GCs, especially when it is multithreaded and the increment/decrement has to be an atomic operation.
- mhh__ 3y agoWith a GC you can basically write whatever you want with reckless abandon and because it's cleaned up at runtime, it's mostly kosher. Without a GC (i.e. rust), in order to be able to make guarantees about things that it cannot determine at compile time (this is a mathematical impossibility) it restricts the set of programs you can write.
- Someone 3y ago> If a programming language can tell when a variable goes out of scope, or its lifetime ends Whether a variable goes out of scope is trivial in many languages. The problem is with determining whether the lifetime of a value ends. For example: func foo(items, moreItems) { b = new bar() if coinflip() { items.append(b) } if coinflip() { moreItems.append(b) } // ‘b’ goes out of scope here, but its value // may live on inside ‘items’ and/or moreItems // and will have to be destroyed when it’s no // longer stored in either. } In general, once you allow for dynamic allocation and references that can be copied (so that multiple objects ‘know’ of the allocated object), it can be very difficult to determine exactly when the last reference to such an object ceases to exist.
- dcuthbertson 3y agoYes, I agree that determining the lifetime of a value is the hard part. I wrote "... and use that information to automatically release allocated memory, ..." to imply the language got lifetime detection right.
- kaba0 3y ago> Does Rust (as a result of not having GC) put restrictions on the kind of code you can write Depending on what you mean by kind: yes. That is, Rust does limit your code’s architecture to a subset that is fine for most things, but not everything.