tezvyn:

Explain Rust Rc and Arc versus Go's tracing GC

AI-drafted, machine-checkedSource: doc.rust-lang.orgadvanced

This tests deterministic reference counting versus tracing GC. A strong answer contrasts Rc's heap reference counts with Go's root tracing, and notes Rc cannot reclaim cycles while Go's GC can. Red flag: claiming Rc has no cycle leak risk.

WHAT THIS TESTS:

This question tests whether you can compare deterministic memory management through reference counting with automatic tracing garbage collection at a systems level. The interviewer wants to see that you understand runtime costs, reclamation semantics, and the specific failure mode of reference cycles, not just API differences between Rust and Go.

A GOOD ANSWER COVERS:

A strong answer hits four things in order. First, explain the Rc mechanism: Rc is a single-threaded smart pointer that stores a heap-allocated value alongside a reference count; every clone increments the count and every drop decrements it, and when the count reaches zero the inner value is deallocated immediately. Second, contrast this with Go's tracing GC: Go does not use reference counts at all; instead its collector periodically pauses or concurrent traces from stack and register roots through the object graph, marking reachable objects and sweeping unreachable ones, which means reclamation is non-deterministic and decoupled from the last variable going out of scope. Third, name Arc as the multi-threaded counterpart that uses atomic operations for its count, while noting that both Rc and Arc share the same fundamental reference-counting semantics. Fourth, identify the key problem: reference cycles. Because Rc only reclaims when the count hits zero, a cycle of two or more objects holding Rc pointers to each other will leak memory forever; Go developers do not worry about this because a tracing collector can identify cycles as unreachable and reclaim them.

COMMON WRONG ANSWERS:

Red flags include claiming that Rc has zero runtime overhead, when in fact every clone and drop incurs a heap write and potential cache miss. Another red flag is asserting that Rc is thread-safe; the canonical text explicitly notes Rc is only for single-threaded scenarios. A third red flag is confusing the cycle problem with borrow checker errors; candidates sometimes say Rust prevents cycles at compile time, which is false for Rc. Finally, stating that Go's GC is strictly slower or faster without nuance shows shallow analysis.

LIKELY FOLLOW-UPS:

An interviewer might ask how you would break a cycle in Rust, which is the standard follow-up to Weak references. They might also ask about the cost of upgrading an Rc to an Arc, or when you would prefer Rc over Box or raw pointers. Another common follow-up is comparing pause times: a tracing GC may introduce stop-the-world pauses, whereas Rc spreads its cost uniformly across clone and drop operations.

ONE CONCRETE EXAMPLE:

Imagine a graph node in Rust defined as struct Node { value: i32, neighbors: Vec<Rc<Node>> }. If node A pushes an Rc to node B and node B pushes an Rc to node A, both reference counts stay at one or higher even when no external variables hold either node. The memory is leaked. In Go, the same pattern with pointers would be collected safely on the next GC cycle because the collector traces from roots and sees the cycle is unreachable.

Read the original → doc.rust-lang.org

Get five bites like this every day.

Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.