rust-systems-programming
Master Rust's memory safety guarantees, ownership model, and systems programming patterns for building reliable, high-performance software. Use when building systems software, performance-critical applications, or learning Rust idioms.
Works with
Agent Skills format with YAML frontmatter. Claude Code reads it as-is.
---
name: "rust-systems-programming"
description: "Master Rust's memory safety guarantees, ownership model, and systems programming patterns for building reliable, high-performance software. Use when building systems software, performance-critical applications, or learning Rust idioms."
license: "MIT"
---
# Rust Systems Programming
Master Rust's unique approach to memory safety through ownership, borrowing, and lifetimes while building high-performance systems software without garbage collection overhead.
## When to Use This Skill
- Building systems software (OS components, drivers, embedded)
- Developing performance-critical applications
- Creating memory-safe concurrent programs
- Writing CLI tools and utilities
- Building WebAssembly applications
- Implementing network services and protocols
- Refactoring C/C++ code for safety
## Core Concepts
### 1. Ownership
**The Foundation of Rust's Memory Safety**
```rust
fn main() {
// Each value has exactly one owner
let s1 = String::from("hello");
// Ownership moves to s2, s1 is no longer valid
let s2 = s1;
// println!("{}", s1); // Error: value borrowed after move
// Clone for deep copy
let s3 = s2.clone();
println!("s2: {}, s3: {}", s2, s3);
// Transfer ownership to function
takes_ownership(s3);
// s3 is no longer valid here
// Primitives implement Copy trait
let x = 5;
let y = x; // Copy, not move
println!("x: {}, y: {}", x, y);
}
fn takes_ownership(s: String) {
println!("{}", s);
} // s is dropped here
```
### 2. Borrowing and References
**Access Without Ownership Transfer**
```rust
fn main() {
let s = String::from("hello");
// Immutable borrow
let len = calculate_length(&s);
println!("Length of '{}' is {}", s, len);
// Mutable borrow
let mut s2 = String::from("hello");
change(&mut s2);
println!("{}", s2);
}
fn calculate_length(s: &String) -> usize {
s.len()
} // s goes out of scope but doesn't drop the value
fn change(s: &mut String) {
s.push_str(", world");
}
// Borrowing rules:
// 1. At any time, you can have EITHER one mutable reference OR any number of immutable references
// 2. References must always be valid
```
### 3. Lifetimes
**Ensuring Reference Validity**
```rust
// Lifetime annotation ensures returned reference lives long enough
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
// Struct with references needs lifetime annotation
struct ImportantExcerpt<'a> {
part: &'a str,
}
impl<'a> ImportantExcerpt<'a> {
fn level(&self) -> i32 {
3
}
// Lifetime elision rules apply
fn announce_and_return_part(&self, announcement: &str) -> &str {
println!("Attention please: {}", 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);
}
```
## Essential Patterns
### Pattern 1: Error Handling with Result
```rust
use std::fs::File;
use std::io::{self, Read};
// Custom error type
#[derive(Debug)]
enum AppError {
IoError(io::Error),
ParseError(String),
NotFound(String),
}
impl From<io::Error> for AppError {
fn from(error: io::Error) -> Self {
AppError::IoError(error)
}
}
// Using Result for error handling
fn read_config(path: &str) -> Result<String, AppError> {
let mut file = File::open(path)?; // ? propagates errors
let mut contents = String::new();
file.read_to_string(&mut contents)?;
Ok(contents)
}
// Combining Results
fn process_file(path: &str) -> Result<i32, AppError> {
let contents = read_config(path)?;
let number: i32 = contents
.trim()
.parse()
.map_err(|_| AppError::ParseError("Invalid number".to_string()))?;
Ok(number * 2)
}
fn main() {
match process_file("config.txt") {
Ok(result) => println!("Result: {}", result),
Err(e) => eprintln!("Error: {:?}", e),
}
}
```
### Pattern 2: Traits and Generics
```rust
use std::fmt::Display;
// Define a trait
trait Summary {
fn summarize(&self) -> String;
// Default implementation
fn summarize_author(&self) -> String {
String::from("(unknown author)")
}
}
struct Article {
headline: String,
content: String,
author: String,
}
impl Summary for Article {
fn summarize(&self) -> String {
format!("{}, by {}", self.headline, self.author)
}
fn summarize_author(&self) -> String {
format!("@{}", self.author)
}
}
// Generic function with trait bounds
fn notify<T: Summary>(item: &T) {
println!("Breaking news! {}", item.summarize());
}
// Multiple trait bounds
fn complex_notify<T: Summary + Display>(item: &T) {
println!("{}", item);
}
// where clause for cleaner syntax
fn some_function<T, U>(t: &T, u: &U) -> i32
where
T: Display + Summary,
U: Clone + Summary,
{
println!("{}", t.summarize());
0
}
// Returning types that implement traits
fn returns_summarizable() -> impl Summary {
Article {
headline: String::from("Breaking"),
content: String::from("Content here"),
author: String::from("Author"),
}
}
```
### Pattern 3: Smart Pointers
```rust
use std::cell::RefCell;
use std::rc::Rc;
// Box<T> - heap allocation
fn box_example() {
let b = Box::new(5);
println!("b = {}", b);
// Recursive types
enum List {
Cons(i32, Box<List>),
Nil,
}
use List::{Cons, Nil};
let list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));
}
// Rc<T> - reference counting for multiple ownership
fn rc_example() {
let a = Rc::new(5);
println!("count after creating a = {}", Rc::strong_count(&a));
let b = Rc::clone(&a);
println!("count after creating b = {}", Rc::strong_count(&a));
{
let c = Rc::clone(&a);
println!("count after creating c = {}", Rc::strong_count(&a));
}
println!("count after c goes out of scope = {}", Rc::strong_count(&a));
}
// RefCell<T> - interior mutability
fn refcell_example() {
let data = RefCell::new(5);
// Borrow mutably at runtime
*data.borrow_mut() += 1;
println!("data = {:?}", data.borrow());
}
// Combining Rc and RefCell for multiple owners with mutability
fn combined_example() {
let value = Rc::new(RefCell::new(5));
let a = Rc::clone(&value);
let b = Rc::clone(&value);
*value.borrow_mut() += 10;
println!("a = {:?}", a.borrow());
println!("b = {:?}", b.borrow());
}
```
### Pattern 4: Concurrency
```rust
use std::sync::{Arc, Mutex, mpsc};
use std::thread;
// Spawning threads
fn basic_threads() {
let handle = thread::spawn(|| {
for i in 1..10 {
println!("hi number {} from spawned thread", i);
thread::sleep(std::time::Duration::from_millis(1));
}
});
handle.join().unwrap();
}
// Move closures for ownership transfer
fn move_closure() {
let v = vec![1, 2, 3];
let handle = thread::spawn(move || {
println!("vector: {:?}", v);
});
handle.join().unwrap();
}
// Message passing with channels
fn channels() {
let (tx, rx) = mpsc::channel();
let tx1 = tx.clone();
thread::spawn(move || {
let vals = vec!["hi", "from", "thread"];
for val in vals {
tx1.send(val.to_string()).unwrap();
thread::sleep(std::time::Duration::from_millis(100));
}
});
thread::spawn(move || {
let vals = vec!["more", "messages"];
for val in vals {
tx.send(val.to_string()).unwrap();
thread::sleep(std::time::Duration::from_millis(100));
}
});
for received in rx {
println!("Got: {}", received);
}
}
// Shared state with Mutex and Arc
fn shared_state() {
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());
}
```
### Pattern 5: Async/Await
```rust
use tokio;
// Async function
async fn fetch_data(url: &str) -> Result<String, reqwest::Error> {
let response = reqwest::get(url).await?;
let body = response.text().await?;
Ok(body)
}
// Concurrent execution
async fn fetch_multiple() {
let urls = vec![
"https://api.example.com/1",
"https://api.example.com/2",
"https://api.example.com/3",
];
let futures: Vec<_> = urls.iter().map(|url| fetch_data(url)).collect();
let results = futures::future::join_all(futures).await;
for result in results {
match result {
Ok(data) => println!("Got: {}", &data[..100.min(data.len())]),
Err(e) => eprintln!("Error: {}", e),
}
}
}
// Tokio runtime
#[tokio::main]
async fn main() {
fetch_multiple().await;
}
```
### Pattern 6: Builder Pattern
```rust
#[derive(Debug)]
struct Server {
host: String,
port: u16,
max_connections: u32,
timeout: u64,
}
#[derive(Default)]
struct ServerBuilder {
host: Option<String>,
port: Option<u16>,
max_connections: Option<u32>,
timeout: Option<u64>,
}
impl ServerBuilder {
fn new() -> Self {
ServerBuilder::default()
}
fn host(mut self, host: impl Into<String>) -> Self {
self.host = Some(host.into());
self
}
fn port(mut self, port: u16) -> Self {
self.port = Some(port);
self
}
fn max_connections(mut self, max: u32) -> Self {
self.max_connections = Some(max);
self
}
fn timeout(mut self, timeout: u64) -> Self {
self.timeout = Some(timeout);
self
}
fn build(self) -> Result<Server, &'static str> {
Ok(Server {
host: self.host.ok_or("host is required")?,
port: self.port.unwrap_or(8080),
max_connections: self.max_connections.unwrap_or(100),
timeout: self.timeout.unwrap_or(30),
})
}
}
fn main() {
let server = ServerBuilder::new()
.host("localhost")
.port(3000)
.max_connections(500)
.build()
.unwrap();
println!("{:?}", server);
}
```
## Best Practices
### 1. Prefer References Over Ownership
```rust
// BAD: Unnecessary ownership transfer
fn process_bad(data: String) {
println!("{}", data);
}
// GOOD: Borrow when you don't need ownership
fn process_good(data: &str) {
println!("{}", data);
}
```
### 2. Use Iterators and Closures
```rust
// Functional style with iterators
let numbers = vec![1, 2, 3, 4, 5];
let sum: i32 = numbers
.iter()
.filter(|&x| x % 2 == 0)
.map(|x| x * 2)
.sum();
println!("Sum of doubled evens: {}", sum);
```
### 3. Handle All Error Cases
```rust
// Use ? operator for propagation
fn read_file(path: &str) -> Result<String, std::io::Error> {
std::fs::read_to_string(path)
}
// Use expect() with meaningful messages
let file = File::open("config.txt")
.expect("Failed to open config.txt - ensure file exists");
```
### 4. Leverage the Type System
```rust
// Newtype pattern for type safety
struct UserId(u64);
struct OrderId(u64);
fn process_user(id: UserId) {
// Can't accidentally pass OrderId
}
```
## Common Pitfalls
- **Fighting the Borrow Checker**: Work with it, not against it
- **Overusing Clone**: Expensive operation, prefer borrowing
- **Ignoring Results**: Always handle potential errors
- **Unnecessary Mutability**: Prefer immutable by default
- **Lifetime Annotation Anxiety**: Compiler often helps with elision
- **Blocking in Async**: Use async-aware operations
## Resources
- The Rust Programming Language (The Book)
- Rust by Example
- Rustlings exercises
- Rust API Guidelines
- Asynchronous Programming in RustMore General & Other skills
find-skills
vercel-labs/skills
Helps users discover and install agent skills when they ask questions like "how do I do X", "find a skill for X", "is there a skill that can...", or express interest in extending capabilities. This skill should be used when the user is looking for functionality that might exist as an installable skill.
grill-me
mattpocock/skills
A relentless interview to sharpen a plan or design.
grill-with-docs
mattpocock/skills
A relentless interview to sharpen a plan or design, which also creates docs (ADR's and glossary) as we go.

