| name | rust-ownership-system |
| user-invocable | false |
| description | Use when Rust's ownership system including ownership rules, borrowing, lifetimes, and memory safety. Use when working with Rust memory management. |
| allowed-tools | ["Bash","Read"] |
Rust Ownership System
Master Rust's unique ownership system that provides memory safety without
garbage collection through compile-time checks.
Ownership Rules
Three fundamental ownership rules:
- Each value in Rust has a variable that's its owner
- There can only be one owner at a time
- When the owner goes out of scope, the value is dropped
fn ownership_basics() {
let s = String::from("hello");
let s2 = s;
println!("{}", s2);
}
Move Semantics
Ownership transfer (move):
fn move_semantics() {
let s1 = String::from("hello");
takes_ownership(s1);
}
fn takes_ownership(s: String) {
println!("{}", s);
}
fn gives_ownership() -> String {
String::from("hello")
}
fn main() {
let s = gives_ownership();
println!("{}", s);
}
Copy trait for stack types:
fn copy_types() {
let x = 5;
let y = x;
println!("x: {}, y: {}", x, y);
let tuple = (1, 2.5, true);
let tuple2 = tuple;
println!("{:?} {:?}", tuple, tuple2);
}
Borrowing
Immutable borrowing (references):
fn immutable_borrow() {
let s1 = String::from("hello");
let len = calculate_length(&s1);
println!("Length of '{}' is {}", s1, len);
}
fn calculate_length(s: &String) -> usize {
s.len()
}
fn multiple_immutable_borrows() {
let s = String::from("hello");
let r1 = &s;
let r2 = &s;
let r3 = &s;
println!("{}, {}, {}", r1, r2, r3);
}
Mutable borrowing:
fn mutable_borrow() {
let mut s = String::from("hello");
change(&mut s);
println!("{}", s);
}
fn change(s: &mut String) {
s.push_str(", world");
}
fn mutable_borrow_rules() {
let mut s = String::from("hello");
let r1 = &mut s;
println!("{}", r1);
}
fn no_mix_borrows() {
let mut s = String::from("hello");
let r1 = &s;
let r2 = &s;
(, r1, r2);
}
Non-lexical lifetimes (NLL):
fn non_lexical_lifetimes() {
let mut s = String::from("hello");
let r1 = &s;
let r2 = &s;
println!("{} {}", r1, r2);
let r3 = &mut s;
println!("{}", r3);
}
Lifetimes
Lifetime annotations:
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
fn main() {
let string1 = String::from("long string");
let string2 = String::from("short");
let result = longest(&string1, &string2);
println!("Longest: {}", result);
}
Lifetime in structs:
struct ImportantExcerpt<'a> {
part: &'a str,
}
impl<'a> ImportantExcerpt<'a> {
fn level(&self) -> i32 {
3
}
fn announce_and_return_part(&self, announcement: &str) -> &str {
println!("Attention: {}", announcement);
self.part
}
}
fn main() {
let novel = String::from("Call me Ishmael. Some years ago...");
let first_sentence = novel.split('.').next().unwrap();
let excerpt = ImportantExcerpt {
part: first_sentence,
};
println!("{}", excerpt.part);
}
Lifetime elision rules:
fn first_word(s: &str) -> &str {
s.split_whitespace().next().unwrap_or("")
}
fn foo(s: &str) -> &str {
s
}
impl<'a> ImportantExcerpt<'a> {
fn get_part(&self) -> &str {
self.part
}
}
Static lifetime:
fn static_lifetime() -> &'static str {
"This string is stored in binary"
}
let s: &'static str = "hello world";
Smart Pointers
Box for heap allocation:
fn box_pointer() {
let b = Box::new(5);
println!("b = {}", b);
}
enum List {
Cons(i32, Box<List>),
Nil,
}
use List::{Cons, Nil};
fn recursive_type() {
let list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));
}
Rc for reference counting:
use std::rc::Rc;
fn rc_example() {
let a = Rc::new(5);
let b = Rc::clone(&a);
let c = Rc::clone(&a);
println!("Reference count: {}", Rc::strong_count(&a));
}
enum RcList {
Cons(i32, Rc<RcList>),
Nil,
}
use RcList::{Cons as RcCons, Nil as RcNil};
fn shared_ownership() {
let a = Rc::new(RcCons(5, Rc::new(RcCons(10, Rc::new(RcNil)))));
let b = RcCons(3, Rc::clone(&a));
let c = RcCons(4, Rc::(&a));
}
RefCell for interior mutability:
use std::cell::RefCell;
fn refcell_example() {
let value = RefCell::new(5);
*value.borrow_mut() += 1;
println!("Value: {}", value.borrow());
}
use std::rc::Rc;
use std::cell::RefCell;
fn rc_refcell() {
let value = Rc::new(RefCell::new(5));
let a = Rc::clone(&value);
let b = Rc::clone(&value);
*a.borrow_mut() += 10;
*b.borrow_mut() += 20;
println!("Value: {}", value.borrow());
}
Ownership Patterns
Taking ownership vs borrowing:
fn consume(s: String) {
println!("{}", s);
}
fn read(s: &String) {
println!("{}", s);
}
fn modify(s: &mut String) {
s.push_str(" modified");
}
fn main() {
let mut s = String::from("hello");
read(&s);
modify(&mut s);
consume(s);
}
Builder pattern with ownership:
struct Config {
name: String,
value: i32,
}
struct ConfigBuilder {
name: Option<String>,
value: Option<i32>,
}
impl ConfigBuilder {
fn new() -> Self {
ConfigBuilder {
name: None,
value: None,
}
}
fn name(mut self, name: String) -> Self {
self.name = Some(name);
self
}
fn value(mut self, value: i32) -> Self {
self.value = Some(value);
self
}
fn build(self) -> Config {
Config {
name: self.name.unwrap_or_default(),
value: self.value.unwrap_or(0),
}
}
}
fn main() {
= ConfigBuilder::()
.(::())
.()
.();
}
Slice Types
String slices:
fn string_slices() {
let s = String::from("hello world");
let hello = &s[0..5];
let world = &s[6..11];
let hello = &s[..5];
let world = &s[6..];
let whole = &s[..];
println!("{} {}", hello, world);
}
fn first_word(s: &str) -> &str {
let bytes = s.as_bytes();
for (i, &item) in bytes.iter().enumerate() {
if item == b' ' {
return &s[..i];
}
}
&s[..]
}
Array slices:
fn array_slices() {
let a = [1, 2, 3, 4, 5];
let slice = &a[1..3];
assert_eq!(slice, &[2, 3]);
}
Clone vs Copy
Understanding Clone trait:
#[derive(Clone)]
struct Point {
x: f64,
y: f64,
}
fn clone_example() {
let p1 = Point { x: 1.0, y: 2.0 };
let p2 = p1.clone();
println!("{} {}", p1.x, p2.x);
}
Copy trait limitations:
#[derive(Copy, Clone)]
struct Coord {
x: i32,
y: i32,
}
struct Person {
name: String,
}
Drop Trait
Custom cleanup with Drop:
struct CustomSmartPointer {
data: String,
}
impl Drop for CustomSmartPointer {
fn drop(&mut self) {
println!("Dropping CustomSmartPointer with data: {}", self.data);
}
}
fn main() {
let c = CustomSmartPointer {
data: String::from("my stuff"),
};
let d = CustomSmartPointer {
data: String::from("other stuff"),
};
println!("CustomSmartPointers created");
}
Manual drop:
fn manual_drop() {
let c = CustomSmartPointer {
data: String::from("some data"),
};
println!("Before drop");
drop(c);
println!("After drop");
}
When to Use This Skill
Use rust-ownership-system when you need to:
- Understand Rust's memory management model
- Write memory-safe code without garbage collection
- Handle ownership transfer between functions
- Work with references and borrowing
- Implement structs with lifetime parameters
- Use smart pointers (Box, Rc, RefCell)
- Debug borrow checker errors
- Choose between ownership, borrowing, and cloning
- Implement custom Drop behavior
- Work with slices and references safely
Best Practices
- Prefer borrowing over ownership transfer when possible
- Use immutable borrows by default, mutable only when needed
- Keep borrow scopes as small as possible
- Use lifetime elision when compiler can infer lifetimes
- Choose appropriate smart pointer for use case
- Avoid RefCell in performance-critical code
- Use slices instead of owned types in function signatures
- Clone only when necessary (it's explicit and visible)
- Implement Drop for custom cleanup logic
- Let compiler guide you with borrow checker errors
Common Pitfalls
- Moving value and trying to use it afterward
- Creating multiple mutable borrows simultaneously
- Mixing mutable and immutable borrows
- Returning references to local variables
- Fighting the borrow checker instead of understanding it
- Overusing clone() to avoid ownership issues
- Not understanding lifetime relationships
- Circular references with Rc (use Weak)
- Panicking with RefCell borrow violations at runtime
- Using 'static lifetime incorrectly
Resources