Skip to content

No Garbage Collector

Every language you know has a garbage collector. Rust does not.

Section titled “Every language you know has a garbage collector. Rust does not.”

Node.js, Python, Go, Java, C# — every mainstream language you have likely written runs on a GC. The GC periodically scans memory, finds unreachable objects, and frees them. This is safe and convenient, but it has costs:

  • Pause latency: GC stop-the-world pauses can spike latency unpredictably.
  • Throughput overhead: GC work competes with your program for CPU time.
  • Memory overhead: GC runtimes keep extra data structures to track live objects.
  • Non-determinism: You cannot predict when an object will be freed.

Rust has none of this. It achieves memory safety through the ownership model alone. The compiler proves — statically — that memory is managed correctly, and generates code to free each allocation at exactly the right place.

You can implement the Drop trait to run custom cleanup logic when a value goes out of scope — the Rust equivalent of a destructor:

TypeScript
// TypeScript — no deterministic cleanup by default
class Resource {
constructor(private name: string) {
console.log(`acquiring: ${name}`);
}
// No guaranteed destructor. You need try/finally or 'using' (ES2023)
release() {
console.log(`releasing: ${this.name}`);
}
}
function useResource() {
const r = new Resource("file handle");
console.log("using resource");
r.release(); // easy to forget; not called on exception
}
useResource();
Rust
struct Resource {
name: String,
}
impl Resource {
fn new(name: &str) -> Self {
println!("acquiring: {}", name);
Resource { name: name.to_string() }
}
}
impl Drop for Resource {
fn drop(&mut self) {
println!("releasing: {}", self.name);
}
}
fn main() {
println!("start");
{
let _r = Resource::new("file handle");
println!("using resource");
} // _r dropped HERE — drop() called automatically
println!("end — resource already released");
}

Deterministic vs non-deterministic cleanup

Section titled “Deterministic vs non-deterministic cleanup”
flowchart TD
  subgraph TS["TypeScript / Node.js"]
    a1["Object created"] --> a2["..."] --> a3["GC runs at some unknown time"] --> a4["memory freed"]
  end
  subgraph RS["Rust"]
    b1["Value created"] --> b2["owner goes out of scope"] --> b3["memory freed IMMEDIATELY"]
  end
Deterministic vs non-deterministic cleanup

This determinism is why Rust is used for OS kernels, game engines, WebAssembly runtimes, and databases. When a file handle or network socket needs to be closed at a precise moment, “the GC will handle it eventually” is not acceptable.

struct Resource {
name: String,
}
impl Resource {
fn new(name: &str) -> Self {
println!("acquiring: {}", name);
Resource { name: name.to_string() }
}
}
impl Drop for Resource {
fn drop(&mut self) {
println!("releasing: {}", self.name);
}
}
fn main() {
println!("start");
{
let _r = Resource::new("file handle");
println!("using resource");
}
println!("end — resource already released");
}
What does RAII stand for and what does it mean in Rust?
When is the `drop` function called for a value in Rust?
What is the main advantage of Rust's ownership-based memory model over a garbage collector?