πŸ—οΈ

Ownership β€” Doing What Ruby's GC Did for You

The core Rust concept Rubyists struggle with most

Have you ever thought about memory management in Ruby? Probably not. GC handles it. Create objects, assign to variables, pass to methods, GC cleans up when you're done.

Rust has no GC. Instead, three ownership rules manage memory.

Rule 1: Every value has one owner

let s1 = String::from("hello");
let s2 = s1;  // ownership moves from s1 to s2
// println!("{}", s1);  // compile error! s1 no longer valid

In Ruby, s2 = s1 makes both point to the same object. Both usable. In Rust, s1 disappears. Only s2 remains.

This is the most shocking part for Rubyists. "I just assigned it to a variable and the original is gone?"

Why this design

Ruby's GC tracks "is anyone still using this object?" at runtime. That costs CPU time and memory. Rust does this tracking at compile time, making runtime cost zero.

Rule 2: Borrowing β€” passing by reference

You can lend without transferring ownership.

fn print_len(s: &String) {  // & means borrow
    println!("{}", s.len());
}

let s = String::from("hello");
print_len(&s);  // lend with &
println!("{}", s);  // s still usable!

& means "I'll just read, you keep ownership." In Ruby, this distinction wasn't needed β€” all object passing is by reference.

Rule 3: Only one mutable borrow

let mut s = String::from("hello");
let r1 = &s;      // immutable ref β€” OK
let r2 = &s;      // another immutable ref β€” OK
// let r3 = &mut s;  // mutable ref β€” error! can't while immutable refs exist

"Multiple readers OK, one writer OK, but no writing while anyone's reading." This rule prevents data races at compile time.

Clone β€” Ruby's .dup

When you don't want to transfer ownership and borrowing doesn't fit β€” copy.

let s1 = String::from("hello");
let s2 = s1.clone();  // deep copy
println!("s1: {}, s2: {}", s1, s2);  // both OK

Same as Ruby's .dup/.clone. But has performance cost β€” use only when needed.

Copy β€” stack values are the exception

Small values like integers, booleans, floats are auto-copied.

let x = 42;
let y = x;  // copied (not moved)
println!("{}", x);  // OK! integers implement Copy trait

Stack values are cheap to copy, so Rust copies automatically. String is heap data β€” no auto-copy.

Practical pattern

Think in this order:

  1. Can I borrow with &? β†’ Usually sufficient
  2. Must I transfer ownership? β†’ Only when function needs to own
  3. Do I need clone? β†’ When neither above works

Key Points

1

Every value has one owner β€” let s2 = s1; moves ownership

2

Borrow with & keeps ownership β€” usually sufficient

3

Multiple immutable refs OK, one mutable only β€” prevents data races

4

clone() for deep copy β€” like Ruby .dup

Pros

  • Memory safety without GC β€” zero runtime cost
  • Data races caught at compile time

Cons

  • Steep initial learning curve since Ruby has no equivalent concept
  • Fighting the compiler takes significant time initially

Use Cases

Passing data to functions β€” deciding borrow vs move Sharing data in multi-threaded code