ChatGPT解决这个技术问题 Extra ChatGPT

What does Rust have instead of a garbage collector?

I understand Rust doesn't have a garbage collector and am wondering how memory is freed up when a binding goes out of scope.

So in this example, I understand that Rust reclaims the memory allocated to a when it goes out of scope.

{
    let a = 4
}

The problem I am having with this, is firstly how this happens, and secondly isn't this a sort of garbage collection? How does it differ from typical garbage collection?

"Deterministic object lifetimes". Similar as C++.
@user2864740 That guide is well out of date. The modern replacement would probably be doc.rust-lang.org/book/references-and-borrowing.html.
Rust is garbage collected, like any other practical programming language. It's just that everybody thinks about garbage collection the wrong way.

A
Ayonix

Garbage collection is typically used periodically or on demand, like if the heap is close to full or above some threshold. It then looks for unused variables and frees their memory, depending on the algorithm.

Rust would know when the variable gets out of scope or its lifetime ends at compile time and thus insert the corresponding LLVM/assembly instructions to free the memory.

Rust also allows some kind of garbage collection, like atomic reference counting though.


By allocating memory when introducing variables and freeing memory when the memory is no longer needed? I don't really know what you want to say with that. Maybe we have different opinions on what a GC is then.
His question is how Rust's approach differs from a typical GC. So I explained what a GC is and how Rust does it without a GC.
doc.rust-lang.org/book/the-stack-and-the-heap.html explains it pretty well. Yes, many things are in the stack but let alone is no sufficient indicator (see Box). I left that out for the sake of simplicity, since the question was asking generally though
@Amomum Actually Rust doesn't have any anointed new() function like C, they are just static functions, and in particular something like let x = MyStruct::new() creates its object on the stack. The real indicator of heap allocation is Box::new() (or any of the structures that depend on Box).
What other languages handle memory management in a similar way to Rust?
M
Matthias Braun

The basic idea of managing resources (including memory) in a program, whatever the strategy, is that the resources tied to unreachable "objects" can be reclaimed. Beyond memory, those resources can be mutex locks, file handles, sockets, database connections...

Languages with a garbage collector periodically scan the memory (one way or another) to find unused objects, release the resources associated with them, and finally release the memory used by those objects.

Rust does not have a GC, how does it manage?

Rust has ownership. Using an affine type system, it tracks which variable is still holding onto an object and, when such a variable goes out of scope, calls its destructor. You can see the affine type system in effect pretty easily:

fn main() {
    let s: String = "Hello, World!".into();
    let t = s;
    println!("{}", s);
}

Yields:

<anon>:4:24: 4:25 error: use of moved value: `s` [E0382]
<anon>:4         println!("{}", s);

<anon>:3:13: 3:14 note: `s` moved here because it has type `collections::string::String`, which is moved by default
<anon>:3         let t = s;
                     ^

which perfectly illustrates that at any point in time, at the language level, the ownership is tracked.

This ownership works recursively: if you have a Vec<String> (i.e., a dynamic array of strings), then each String is owned by the Vec which itself is owned by a variable or another object, etc... thus, when a variable goes out of scope, it recursively frees up all resources it held, even indirectly. In the case of the Vec<String> this means:

Releasing the memory buffer associated to each String Releasing the memory buffer associated to the Vec itself

Thus, thanks to the ownership tracking, the lifetime of ALL the program objects is strictly tied to one (or several) function variables, which will ultimately go out of scope (when the block they belong to ends).

Note: this is a bit optimistic, using reference counting (Rc or Arc) it is possible to form cycles of references and thus cause memory leaks, in which case the resources tied to the cycle might never be released.


"Languages with a Garbage Collector periodically scan the memory (one way or another)". Many do but that is not true in general. Real-time garbage collectors scan incrementally rather than periodically. Reference counting languages like Mathematica don't scan at all.
"I do not count reference-counting as a complete Garbage Collection mechanism since it must be supplemented to avoid leaking cycles". RC is conventionally regarded as a form of GC. In Mathematica and Erlang, for example, cycles cannot be created by design so RC does not leak. For a high-level perspective, see "A unified theory of garbage collection" cs.virginia.edu/~cs415/reading/bacon-garbage.pdf
"I fail to see how periodic does not cover the incremental case". Stop the world algorithms would be regarded as periodic whereas tricolor marking is regarded as incremental, for example. They are opposites in this context.
@JD You're going way too deep. His explanation doesn't have to do with the internals of how GCs work, only the differences between GC and non-GC languages. The differentiation that you're trying to make is based on the implementation of GCs themselves. The differentiation that he's trying to make is between GCs in the abstract. There's no need to delve 500 words into the semantic meaning of "periodic" in this context.
Abstractly, we normally consider langauges like C++/Rust that use RAII/RC as non-garbage-collecting. And languages such as Java/Python/C# to be garbage collecting (Even if it uses RC as an underlying implementation). The core difference is that in C++/Rust, the RC is explicit, and it's virtually a 5-line wrapper around calling malloc and free yourself. When in a GC language, it's abstracted from view, and classes are passed by reference rather than by value. (And the language specification rarely mentioned whether or not its RC or Mark-and-sweep, that's normally an implementation detail)
S
Swiss

With a language where you must manually manage memory, the distinction between the stack and the heap becomes critical. Every time you call a function, enough space is allocated on the stack for all variables contained within the scope of that function. When the function returns, the stack frame associated with that function is "popped" off the stack, and the memory is freed for future use.

From a practical standpoint, this inadvertent memory cleaning is used as a means of automatic memory storage that will be cleared at the end of the function's scope.

There is more information available here: https://doc.rust-lang.org/book/the-stack-and-the-heap.html


While using the stack is handy, deterministic object lifetimes can still be handled if all values were 'created on the heap'. Thus it is an implementation detail; not necessarily a language strategy.
You keep using that word. I do not think it means what you think it means.
Means what I wish to express; being the opposite of non-deterministic lifetimes. Make an offer for a better phrase.
Thanks for the answer, i've give the points to the first one simply because it was submitted first. The information is just as useful and valid.
@user2864740 Deterministic object lifetimes refers to being able to tell exactly when the object's memory will be cleared once its destructor has been called. It has nothing to do with how that destructor is called in the first place. You keep bringing up the same term repeatedly even though it has no direct significance to the question.
Y
Yilmaz

Some languages have reference counting, some have garbage collectors. Rust avoids both, instead, it allows only a single variable name or alias if you like to own a memory location at any point in time. You can move the ownership from one variable name to another, but you can’t have two variable names pointing to the same memory address (Except for shared Ownership. Rust provides the reference-counted pointer types Rc and Arc. Because sometimes it’s difficult to find every value a single owner that has the lifetime you need).

Rust uses a relatively unique memory management approach that incorporates the idea of memory “ownership”. Basically, Rust keeps track of who can read and write to memory. It knows when the program is using memory and immediately frees the memory once it is no longer needed. It enforces memory rules at compile time, making it virtually impossible to have runtime memory bugs. You do not need to manually keep track of memory. The compiler takes care of it.

Discord recently switched from Go to Rust in one of its services just because garbage collector was causing latency. They explained very well why they did this and you will learn more about the garbage collector and rust memory system:

https://discord.com/blog/why-discord-is-switching-from-go-to-rust#:~:text=Discord%20is%20a%20product%20focused,and%20messages%20you%20have%20read.


"you can’t have two variable names pointing to the same memory address" - I'm pretty sure that is exactly what Rc/Arc do.
Rc and Arc, allow values to have multiple owners, under some Restrictions.
I don't see how that invalidates my previous comment. Either way, your comment is in conflict with your statement: "Every value has a single owner".
@IInspectable I updated the answer. Is it better?