Resource acquisition is initialization (RAII) ties a resource to an object's lifetime: acquire it when the object is initialized, then release it when the object is destroyed. Its practical value is cleanup that follows scope, including when a function returns early. In C, returning after a failed file-open attempt can leave previously allocated memory unfreed; a C++ smart pointer instead releases its resource when it leaves scope. section 4.1 section 4.1.1
In Rust, ownership and move semantics make the cleanup point easier to reason about. When an owning value leaves scope, Rust calls its destructor; implement the Drop trait to give a resource-owning type custom cleanup. A move transfers ownership rather than creating a second owner. Copy values are an important exception: they cannot implement Drop, so do not depend on destruction of a Copy value to release a resource. Unsafe code also qualifies the usual RAII guarantees. section 4.1.2 section 4.7
A moved value in a container
Consider `let status = String::from("Active"); let statuses = vec![status];`. The String owns allocated memory, and placing it in the Vec moves ownership into the container. When `statuses` leaves scope, the container's destruction also destroys its contents. This is why the original `status` is no longer a separate usable owner after the move. section 4.1.2
RAII is useful beyond heap memory:
- Box and Vec manage allocated memory; Rc and Arc use destruction as part of reference counting. section 4.1.3
- Ref and RefMut guard RefCell borrows; MutexGuard releases a mutex lock when destroyed. Read-write locks likewise return guards for read or write access. section 4.1.3
Listing 4.2 RAII in Rust with Mutex and Condvar
use std::sync::{Arc, Condvar, Mutex};
use std::thread;
fn main() {
let outer = Arc::new(
(Mutex::new(0), Condvar::new()) #1
);
let inner = outer.clone();
thread::spawn(move || {
let (mutex, cond_var) = &*inner; #2
let mut guard = mutex.lock().unwrap(); #3
*guard += 1; #4
println!("inner guard={guard}");
cond_var.notify_one(); #5
}); #6
let (mutex, cond_var) = &*outer;
let mut guard = mutex.lock().unwrap(); #7
println!("outer before wait guard={guard}");
while *guard == 0 { #8
guard = cond_var.wait(guard).unwrap(); #9
}
println!("outer after wait guard={guard}");
}
The threaded Mutex and Condvar example combines several lifetimes: Arc shares ownership, a MutexGuard holds the lock, and waiting passes that guard to Condvar. Follow where each guard and Arc value leaves scope to see which resource is released. section 4.1.3
Use RAII when a type should reliably own a resource and clean it up at destruction. For a custom container or other resource-owning type, Drop supplies that cleanup point; for locks and borrows, use the guards returned by the relevant APIs rather than managing release separately. section 4.1.3 section 4.7



