Files
Sprimo/skills/m01-ownership/comparison.md
2026-02-12 22:58:33 +08:00

5.6 KiB

Ownership: Comparison with Other Languages

Rust vs C++

Memory Management

Aspect Rust C++
Default Move semantics Copy semantics (pre-C++11)
Move let b = a; (a invalidated) auto b = std::move(a); (a valid but unspecified)
Copy let b = a.clone(); auto b = a;
Safety Compile-time enforcement Runtime responsibility

Rust Move vs C++ Move

// Rust: after move, 'a' is INVALID
let a = String::from("hello");
let b = a;  // a moved
// println!("{}", a);  // COMPILE ERROR

// Equivalent in C++:
// std::string a = "hello";
// std::string b = std::move(a);
// std::cout << a;  // UNDEFINED (compiles but buggy)

Smart Pointers

Rust C++ Purpose
Box<T> std::unique_ptr<T> Unique ownership
Rc<T> std::shared_ptr<T> Shared ownership
Arc<T> std::shared_ptr<T> + atomic Thread-safe shared
RefCell<T> (manual runtime checks) Interior mutability

Rust vs Go

Memory Model

Aspect Rust Go
Memory Stack + heap, explicit GC manages all
Ownership Enforced at compile-time None (GC handles)
Null Option<T> nil for pointers
Concurrency Send/Sync traits Channels (less strict)

Sharing Data

// Rust: explicit about sharing
use std::sync::Arc;
let data = Arc::new(vec![1, 2, 3]);
let data_clone = Arc::clone(&data);
std::thread::spawn(move || {
    println!("{:?}", data_clone);
});

// Go: implicit sharing
// data := []int{1, 2, 3}
// go func() {
//     fmt.Println(data)  // potential race condition
// }()

Why No GC in Rust

  1. Deterministic destruction: Resources freed exactly when scope ends
  2. Zero-cost: No GC pauses or overhead
  3. Embeddable: Works in OS kernels, embedded systems
  4. Predictable latency: Critical for real-time systems

Rust vs Java/C#

Reference Semantics

Aspect Rust Java/C#
Objects Owned by default Reference by default
Null Option<T> null (nullable)
Immutability Default Must use final/readonly
Copy Explicit .clone() Reference copy (shallow)

Comparison

// Rust: clear ownership
fn process(data: Vec<i32>) {  // takes ownership
    // data is ours, will be freed at end
}

let numbers = vec![1, 2, 3];
process(numbers);
// numbers is invalid here

// Java: ambiguous ownership
// void process(List<Integer> data) {
//     // Who owns data? Caller? Callee? Both?
//     // Can caller still use it?
// }

Rust vs Python

Memory Model

Aspect Rust Python
Typing Static, compile-time Dynamic, runtime
Memory Ownership-based Reference counting + GC
Mutability Default immutable Default mutable
Performance Native, zero-cost Interpreted, higher overhead

Common Pattern Translation

// Rust: borrowing iteration
let items = vec!["a", "b", "c"];
for item in &items {
    println!("{}", item);
}
// items still usable

// Python: iteration doesn't consume
// items = ["a", "b", "c"]
// for item in items:
//     print(item)
// items still usable (different reason - ref counting)

Unique Rust Concepts

Concepts Other Languages Lack

  1. Borrow Checker: No other mainstream language has compile-time borrow checking
  2. Lifetimes: Explicit annotation of reference validity
  3. Move by Default: Values move, not copy
  4. No Null: Option<T> instead of null pointers
  5. Affine Types: Values can be used at most once

Learning Curve Areas

Concept Coming From Key Insight
Ownership GC languages Think about who "owns" data
Borrowing C/C++ Like references but checked
Lifetimes Any Explicit scope of validity
Move C++ Move is default, not copy

Mental Model Shifts

From GC Languages (Java, Go, Python)

Before: "Memory just works, GC handles it"
After:  "I explicitly decide who owns data and when it's freed"

Key shifts:

  • Think about ownership at design time
  • Returning references requires lifetime thinking
  • No more null - use Option<T>

From C/C++

Before: "I manually manage memory and hope I get it right"
After:  "Compiler enforces correctness, I fight the borrow checker"

Key shifts:

  • Trust the compiler's errors
  • Move is the default (unlike C++ copy)
  • Smart pointers are idiomatic, not overhead

From Functional Languages (Haskell, ML)

Before: "Everything is immutable, copying is fine"
After:  "Mutability is explicit, ownership prevents aliasing"

Key shifts:

  • Mutability is safe because of ownership rules
  • No persistent data structures needed (usually)
  • Performance characteristics are explicit

Performance Trade-offs

Language Memory Overhead Latency Throughput
Rust Minimal (no GC) Predictable Excellent
C++ Minimal Predictable Excellent
Go GC overhead GC pauses Good
Java GC overhead GC pauses Good
Python High (ref counting + GC) Variable Lower

When Rust Ownership Wins

  1. Real-time systems: No GC pauses
  2. Embedded: No runtime overhead
  3. High-performance: Zero-cost abstractions
  4. Concurrent: Data races prevented at compile time

When GC Might Be Preferable

  1. Rapid prototyping: Less mental overhead
  2. Complex object graphs: Cycles are tricky in Rust
  3. GUI applications: Object lifetimes are dynamic
  4. Small programs: Overhead doesn't matter