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.
What “drop” looks like
Section titled “What “drop” looks like”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 — no deterministic cleanup by defaultclass 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();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 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.
Try it
Section titled “Try it”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");}Compiling…