Claude
Skills
Sign in
Back

rust-systems-programming

Included with Lifetime
$97 forever

Complete guide for Rust systems programming including ownership, borrowing, concurrency, async programming, unsafe code, and performance optimization

Backend & APIsrustsystems-programmingownershipconcurrencyasyncmemory-safetyperformance

What this skill does


# Rust Systems Programming

A comprehensive skill for building high-performance, memory-safe systems software using Rust. This skill covers ownership, borrowing, concurrency, async programming, unsafe code, FFI, and performance optimization for systems-level development.

## When to Use This Skill

Use this skill when:

- Building systems software requiring memory safety without garbage collection
- Developing high-performance applications with zero-cost abstractions
- Writing concurrent or parallel programs with data race prevention
- Creating async/await applications for I/O-bound workloads (web servers, databases)
- Working with low-level code, FFI, or hardware interfaces
- Replacing C/C++ code with safer alternatives
- Building command-line tools, network services, or embedded systems
- Optimizing performance-critical sections of applications
- Creating libraries that guarantee memory safety at compile time
- Developing WebAssembly modules for near-native performance

## Core Concepts

### The Ownership Model

Rust's ownership system is the foundation of its memory safety guarantees:

**Ownership Rules:**
1. Each value in Rust has exactly one owner
2. When the owner goes out of scope, the value is dropped
3. Ownership can be transferred (moved) to new owners

**Move Semantics:**

```rust
struct MyStruct { s: u32 }

fn main() {
    let mut x = MyStruct{ s: 5u32 };
    let y = x;  // Ownership moved from x to y
    // x.s = 6;  // ERROR: x is no longer valid
    // println!("{}", x.s);  // ERROR: cannot use x after move
}
```

When a type doesn't implement `Copy`, assignment moves ownership rather than copying. This prevents double-free errors and use-after-move bugs at compile time.

**For Copy Types:**

```rust
let x = 5;  // i32 implements Copy
let y = x;  // x is copied, not moved
println!("{}", x);  // OK: x is still valid
```

### Borrowing and References

Borrowing allows temporary access to data without taking ownership:

**Immutable Borrowing:**

```rust
fn main() {
    let s1 = String::from("hello");

    let len = calculate_length(&s1);  // Borrow s1 immutably

    println!("The length of '{}' is {}.", s1, len);  // s1 still valid
}

fn calculate_length(s: &String) -> usize {
    s.len()  // Can read but not modify
}
```

**Mutable Borrowing:**

Rust enforces exclusive mutable access to prevent data races:

```rust
fn main() {
    let mut value = 3;
    let borrow = &mut value;  // Mutable borrow
    *borrow += 1;
    println!("{}", borrow);  // 4

    // value is accessible again after borrow ends
}
```

**Borrowing Rules:**
- You can have either one mutable reference OR any number of immutable references
- References must always be valid (no dangling pointers)
- Mutable and immutable borrows cannot coexist

**Common Borrowing Error:**

```rust
fn main() {
    let mut value = 3;
    // Create a mutable borrow of `value`.
    let borrow = &mut value;
    let _sum = value + 1; // ERROR: cannot use `value` because
                          //        it was mutably borrowed
    println!("{}", borrow);
}
```

### Lifetimes

Lifetimes ensure references are always valid:

```rust
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}
```

The `'a` lifetime parameter tells the compiler that the returned reference will be valid as long as both input references are valid.

### Ownership Patterns for Sharing

**Rc (Reference Counted) for Single-threaded Shared Ownership:**

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

struct MyStruct { s: u32 }

fn main() {
    let mut x = Rc::new(RefCell::new(MyStruct{ s: 5u32 }));
    let y = x.clone();  // Increment reference count
    x.borrow_mut().s = 6;  // Interior mutability via RefCell
    println!("{}", x.borrow().s);
}
```

`Rc<T>` provides shared ownership with reference counting. `RefCell<T>` enables interior mutability, enforcing borrow rules at runtime rather than compile time.

**Arc (Atomic Reference Counted) for Thread-safe Sharing:**

```rust
use std::sync::Arc;
use std::thread;

struct FancyNum {
    num: u8,
}

fn main() {
    let fancy_ref1 = Arc::new(FancyNum { num: 5 });
    let fancy_ref2 = fancy_ref1.clone();

    let x = thread::spawn(move || {
        // `fancy_ref1` can be moved and has a `'static` lifetime
        println!("child thread: {}", fancy_ref1.num);
    });

    x.join().expect("child thread should finish");
    println!("main thread: {}", fancy_ref2.num);
}
```

`Arc<T>` is the thread-safe version of `Rc<T>`, using atomic operations for reference counting.

### Box for Heap Allocation

```rust
Box<T>
```

`Box<T>` is an owning pointer that allocates `T` on the heap. Useful for:
- Recursive types with known size
- Large values that should not be copied on the stack
- Trait objects with dynamic dispatch

## Concurrency Patterns

### Threads and Message Passing

Rust prevents data races at compile time through its ownership system:

```rust
use std::thread;
use std::sync::mpsc;

fn main() {
    let (tx, rx) = mpsc::channel();

    thread::spawn(move || {
        tx.send("Hello from thread").unwrap();
    });

    let message = rx.recv().unwrap();
    println!("{}", message);
}
```

### Shared State with Mutex

```rust
use std::sync::{Arc, Mutex};
use std::thread;

fn main() {
    let counter = Arc::new(Mutex::new(0));
    let mut handles = vec![];

    for _ in 0..10 {
        let counter = Arc::clone(&counter);
        let handle = thread::spawn(move || {
            let mut num = counter.lock().unwrap();
            *num += 1;
        });
        handles.push(handle);
    }

    for handle in handles {
        handle.join().unwrap();
    }

    println!("Result: {}", *counter.lock().unwrap());
}
```

**Key Concurrency Types:**
- `Mutex<T>`: Mutual exclusion lock for shared mutable state
- `RwLock<T>`: Reader-writer lock allowing multiple readers or one writer
- `Arc<T>`: Atomic reference counting for thread-safe sharing
- `mpsc`: Multi-producer, single-consumer channels

### Data Race Prevention

**ThreadSanitizer Example:**

```rust
static mut A: usize = 0;

fn main() {
    let t = std::thread::spawn(|| {
        unsafe { A += 1 };
    });
    unsafe { A += 1 };

    t.join().unwrap();
}
```

This code has a data race. ThreadSanitizer (enabled with `RUSTFLAGS=-Zsanitizer=thread`) detects concurrent access to static mutable data:

```
WARNING: ThreadSanitizer: data race (pid=10574)
  Read of size 8 at 0x5632dfe3d030 by thread T1:
  Previous write of size 8 at 0x5632dfe3d030 by main thread:
```

## Async Programming

### Async/Await Basics

Async programming in Rust allows concurrent I/O without blocking threads:

```rust
async fn foo(n: usize) {
    if n > 0 {
        Box::pin(foo(n - 1)).await;
    }
}
```

Recursive async functions require `Box::pin()` to give the future a known size.

### Async Closures

**Async Closure with Move Semantics:**

```rust
fn force_fnonce<T: async FnOnce()>(t: T) -> T { t }

let x = String::new();
let c = force_fnonce(async move || {
    println!("{x}");
});
```

When constrained to `AsyncFnOnce`, the closure captures by move to ensure proper ownership.

**Async Closure Borrowing:**

```rust
let x = &1i32; // Lifetime '1
let c = async move || {
    println!("{:?}", *x);
    // Even though the closure moves x, we're only capturing *x,
    // so the inner coroutine can reborrow the data for its original lifetime.
};
```

**Mutable Borrowing in Async Closures:**

```rust
let mut x = 1i32;
let c = async || {
    x = 1;
    // The parent borrows `x` mutably.
    // When we call `c()`, we implicitly autoref for `AsyncFnMut::async_call_mut`.
    // The inner coroutine captures with the lifetime of the coroutine-closure.
};
```

### Common Async Errors

**E0373: Async block capturing short-lived variable:**

```rust
use std::future::Future;

async fn f() {
    let v = vec![1, 2, 3i32];
    spawn(async { //~ ERROR E0373
        println!("{:?}", v)  // v might go out of scope befo

Related in Backend & APIs