Skip to content

Rust Ownership

Overview

Ownership is Rust's most unique and important feature, enabling Rust to guarantee memory safety without a garbage collector. This chapter will delve into how the ownership system works and how to use it.

🎯 Ownership Basic Concepts

Ownership Rules

rust
// Three rules of Rust ownership:
// 1. Every value in Rust has a variable called its owner
// 2. A value can have only one owner at any given time
// 3. When the owner (variable) goes out of scope, the value is dropped

fn ownership_rules() {
    // Rule 1: Each value has an owner
    let s = String::from("hello"); // s is the owner of "hello"

    // Rule 2: Only one owner at the same time
    let s2 = s; // Ownership transfers from s to s2

    // println!("{}", s); // Compile error! s is no longer valid
    println!("{}", s2); // OK, s2 is now the owner

} // Rule 3: s2 goes out of scope, memory is freed

Variable and Data Interaction

Move

rust
fn move_semantics() {
    // Basic types: copy
    let x = 5;
    let y = x; // x is copied to y, both are valid
    println!("x: {}, y: {}", x, y);

    // Complex types: move
    let s1 = String::from("hello");
    let s2 = s1; // s1's ownership moves to s2

    // println!("{}", s1); // Compile error! s1 is invalid
    println!("{}", s2);

    // Explanation of why move happens
    // String structure on the stack:
    // name     value
    // ptr      -> pointer to heap data
    // len      -> string length
    // capacity -> allocated capacity

    // If Rust allowed copying, there would be two pointers to the same heap memory
    // When both variables go out of scope, they would both try to free memory, causing a double free error
}

Clone

rust
fn clone_semantics() {
    let s1 = String::from("hello");
    let s2 = s1.clone(); // Deep copy, creates new heap data

    println!("s1: {}, s2: {}", s1, s2); // Both are valid

    // Cost of cloning
    let large_string = "a".repeat(1_000_000);
    let cloned = large_string.clone(); // Expensive operation
    println!("Original length: {}, Cloned length: {}", large_string.len(), cloned.len());
}

Copy

rust
fn copy_trait() {
    // Types implementing Copy trait are copied instead of moved
    let x = 5;
    let y = x; // Copy, not move
    println!("x: {}, y: {}", x, y);

    // Copy types include:
    // - All integer types
    // - Boolean type
    // - Floating point types
    // - Character type
    // - Tuples (if all elements are Copy)

    let tuple = (1, 2.0, 'a');
    let tuple_copy = tuple; // Copy
    println!("Original: {:?}, Copy: {:?}", tuple, tuple_copy);

    // Cannot implement both Copy and Drop
    // If a type implements Drop trait, it cannot implement Copy trait
}

// Copy for custom types
#[derive(Copy, Clone, Debug)]
struct Point {
    x: i32,
    y: i32,
}

fn custom_copy() {
    let p1 = Point { x: 1, y: 2 };
    let p2 = p1; // Copy, not move

    println!("p1: {:?}, p2: {:?}", p1, p2);
}

🔄 Ownership and Functions

Passing Values to Functions

rust
fn ownership_and_functions() {
    let s = String::from("hello");
    takes_ownership(s); // s's value moves into the function
    // println!("{}", s); // Compile error! s is no longer valid

    let x = 5;
    makes_copy(x); // x's value is copied into the function
    println!("{}", x); // x is still valid
}

fn takes_ownership(some_string: String) { // some_string comes into scope
    println!("{}", some_string);
} // some_string goes out of scope and is dropped

fn makes_copy(some_integer: i32) { // some_integer comes into scope
    println!("{}", some_integer);
} // some_integer goes out of scope, nothing special happens

Return Values and Scope

rust
fn return_values_and_scope() {
    let s1 = gives_ownership(); // Function return value transfers to s1

    let s2 = String::from("hello");
    let s3 = takes_and_gives_back(s2); // s2 moves into function, return value transfers to s3

    println!("s1: {}, s3: {}", s1, s3);
    // println!("{}", s2); // Compile error! s2 has been moved
}

fn gives_ownership() -> String {
    let some_string = String::from("hello");
    some_string // Return, ownership moves to calling function
}

fn takes_and_gives_back(a_string: String) -> String {
    a_string // Return, ownership moves to calling function
}

Complex Ownership Transfers

rust
fn complex_ownership_transfer() {
    let mut data = vec![1, 2, 3, 4, 5];

    // Functions can return multiple values
    let (data, length) = calculate_length_and_return(data);
    println!("Data: {:?}, Length: {}", data, length);

    // Use tuples to return multiple values
    let result = process_data(data);
    match result {
        Some((processed_data, summary)) => {
            println!("Processed data: {:?}", processed_data);
            println!("Summary: {}", summary);
        }
        None => println!("Processing failed"),
    }
}

fn calculate_length_and_return(vec: Vec<i32>) -> (Vec<i32>, usize) {
    let length = vec.len();
    (vec, length) // Return tuple, ownership transfers
}

fn process_data(mut data: Vec<i32>) -> Option<(Vec<i32>, String)> {
    if data.is_empty() {
        return None;
    }

    // Modify data
    for item in &mut data {
        *item *= 2;
    }

    let summary = format!("Processed {} elements", data.len());
    Some((data, summary))
}

🏗️ Ownership and Data Structures

Struct Ownership

rust
#[derive(Debug)]
struct User {
    username: String,
    email: String,
    sign_in_count: u64,
    active: bool,
}

fn struct_ownership() {
    let user1 = User {
        email: String::from("someone@example.com"),
        username: String::from("someusername123"),
        active: true,
        sign_in_count: 1,
    };

    // Move entire struct
    let user2 = user1;
    // println!("{:?}", user1); // Compile error! user1 has been moved
    println!("{:?}", user2);

    // Partial move
    let user3 = User {
        email: String::from("another@example.com"),
        username: String::from("anotherusername"),
        active: true,
        sign_in_count: 1,
    };

    let email = user3.email; // Only move email field
    let username = user3.username; // Only move username field

    // println!("{:?}", user3); // Compile error! Some fields have been moved
    // But can access non-moved fields
    println!("Active status: {}, Sign in count: {}", user3.active, user3.sign_in_count);
}

Enum Ownership

rust
#[derive(Debug)]
enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
    ChangeColor(i32, i32, i32),
}

fn enum_ownership() {
    let msg1 = Message::Write(String::from("hello"));
    let msg2 = msg1; // Move

    // println!("{:?}", msg1); // Compile error
    println!("{:?}", msg2);

    // Ownership transfer during matching
    match msg2 {
        Message::Write(text) => {
            println!("Write message: {}", text);
            // text ownership is acquired here
        }
        _ => {}
    }

    // println!("{:?}", msg2); // Compile error! msg2 content has been moved
}

Collection Type Ownership

rust
fn collection_ownership() {
    // Vec ownership
    let mut vec1 = vec![1, 2, 3];
    let vec2 = vec1; // Move

    // println!("{:?}", vec1); // Compile error

    // Remove element from Vec
    let mut vec3 = vec![String::from("hello"), String::from("world")];
    let first = vec3.remove(0); // Move out first element

    println!("Removed element: {}", first);
    println!("Remaining Vec: {:?}", vec3);

    // HashMap ownership
    use std::collections::HashMap;

    let mut map = HashMap::new();
    let key = String::from("favorite");
    let value = String::from("blue");

    map.insert(key, value); // key and value ownership transfers to map
    // println!("{} {}", key, value); // Compile error

    // Retrieve ownership
    if let Some(color) = map.remove("favorite") {
        println!("Favorite color: {}", color);
    }
}

🔀 Ownership Patterns

RAII Pattern

rust
use std::fs::File;
use std::io::prelude::*;

fn raii_pattern() {
    // Resource Acquisition Is Initialization (RAII)
    {
        let mut file = File::create("temp.txt").expect("Failed to create file");
        file.write_all(b"Hello, RAII!").expect("Failed to write");

        // File is automatically closed here because file goes out of scope
    }

    // File has already been closed

    // Custom RAII type
    let _guard = ResourceGuard::new("Important resource");
    // guard will automatically clean up resources when it goes out of scope
}

struct ResourceGuard {
    name: String,
}

impl ResourceGuard {
    fn new(name: &str) -> Self {
        println!("Acquiring resource: {}", name);
        Self {
            name: name.to_string(),
        }
    }
}

impl Drop for ResourceGuard {
    fn drop(&mut self) {
        println!("Releasing resource: {}", self.name);
    }
}

Builder Pattern and Ownership

rust
#[derive(Debug)]
struct Config {
    host: String,
    port: u16,
    ssl: bool,
}

struct ConfigBuilder {
    host: Option<String>,
    port: Option<u16>,
    ssl: Option<bool>,
}

impl ConfigBuilder {
    fn new() -> Self {
        Self {
            host: None,
            port: None,
            ssl: None,
        }
    }

    // Consume self, return new self
    fn host(mut self, host: String) -> Self {
        self.host = Some(host);
        self
    }

    fn port(mut self, port: u16) -> Self {
        self.port = Some(port);
        self
    }

    fn ssl(mut self, ssl: bool) -> Self {
        self.ssl = Some(ssl);
        self
    }

    // Consume self, return final product
    fn build(self) -> Result<Config, String> {
        let host = self.host.ok_or("Missing hostname")?;
        let port = self.port.unwrap_or(80);
        let ssl = self.ssl.unwrap_or(false);

        Ok(Config { host, port, ssl })
    }
}

fn builder_pattern_ownership() {
    let config = ConfigBuilder::new()
        .host(String::from("localhost"))
        .port(8080)
        .ssl(true)
        .build()
        .expect("Failed to build config");

    println!("Config: {:?}", config);
}

⚡ Performance Considerations

Avoiding Unnecessary Moves

rust
fn performance_considerations() {
    // Bad example: unnecessary move
    let data = vec![1, 2, 3, 4, 5];
    let result = process_vec_bad(data);
    // data cannot be used anymore

    // Better example: use references
    let data = vec![1, 2, 3, 4, 5];
    let result = process_vec_good(&data);
    println!("Original data still available: {:?}", data);
    println!("Processing result: {}", result);

    // Only move when modification is needed
    let mut data = vec![1, 2, 3, 4, 5];
    modify_vec(&mut data);
    println!("Modified data: {:?}", data);
}

fn process_vec_bad(vec: Vec<i32>) -> i32 {
    vec.iter().sum() // Doesn't need ownership but moved anyway
}

fn process_vec_good(vec: &[i32]) -> i32 {
    vec.iter().sum() // Only needs to read, use reference
}

fn modify_vec(vec: &mut Vec<i32>) {
    vec.push(6); // Needs to modify, use mutable reference
}

Smart Ownership Management

rust
use std::rc::Rc;
use std::cell::RefCell;

fn smart_ownership() {
    // Use Rc to share ownership
    let data = Rc::new(vec![1, 2, 3, 4, 5]);
    let data1 = Rc::clone(&data);
    let data2 = Rc::clone(&data);

    println!("Reference count: {}", Rc::strong_count(&data));

    // Interior mutability
    let shared_data = Rc::new(RefCell::new(vec![1, 2, 3]));

    {
        let mut data = shared_data.borrow_mut();
        data.push(4);
    }

    println!("Shared data: {:?}", shared_data.borrow());
}

📝 Chapter Summary

Through this chapter, you should have mastered:

Core Ownership Concepts

  • ✅ Three basic rules of ownership
  • ✅ Difference between move, clone, and copy
  • ✅ Ownership and function calls
  • ✅ Ownership transfer with return values

Advanced Ownership Patterns

  • ✅ Struct and enum ownership
  • ✅ Collection type ownership management
  • ✅ RAII resource management pattern
  • ✅ Ownership in builder pattern

Performance Optimization

  • ✅ Avoid unnecessary moves and clones
  • ✅ Choose appropriate parameter passing methods
  • ✅ Smart pointer usage scenarios
  • ✅ Memory management best practices

Best Practices

  1. Prefer references over moves
  2. Clone only when necessary
  3. Use RAII for resource management
  4. Consider ownership when designing APIs

Continue learning: Next Chapter - Rust References and Borrowing

Content is for learning and research only.