Loading...
Loading...
Expert-level Rust development with ownership, lifetimes, async, error handling, and production-grade patterns
npx skill4agent add personamanagmentlayer/pcl rust-expert// Rule 1: Each value has one owner
let s1 = String::from("hello");
let s2 = s1; // s1 is moved, no longer valid
// println!("{}", s1); // ERROR: s1 moved
// Rule 2: When owner goes out of scope, value is dropped
{
let s = String::from("hello");
} // s is dropped here
// Rule 3: Only one mutable reference OR multiple immutable references
let mut s = String::from("hello");
let r1 = &s; // OK
let r2 = &s; // OK
// let r3 = &mut s; // ERROR: cannot borrow as mutable
let mut s = String::from("hello");
let r1 = &mut s; // OK
// let r2 = &mut s; // ERROR: cannot have two mutable references// Immutable borrow
fn calculate_length(s: &String) -> usize {
s.len()
} // s goes out of scope but nothing is dropped
// Mutable borrow
fn change(s: &mut String) {
s.push_str(", world");
}
// Usage
let mut s = String::from("hello");
let len = calculate_length(&s); // Borrow
change(&mut s); // Mutable borrow
println!("{}, length: {}", s, len);
// Returning references (lifetime required)
fn first_word<'a>(s: &'a str) -> &'a str {
let bytes = s.as_bytes();
for (i, &item) in bytes.iter().enumerate() {
if item == b' ' {
return &s[0..i];
}
}
&s[..]
}// Lifetime annotations
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
}
}
// Multiple lifetimes
fn longest<'a, 'b>(x: &'a str, y: &'b str) -> &'a str
where
'b: 'a, // 'b outlives 'a
{
if x.len() > y.len() { x } else { x }
}
// Lifetime elision rules
fn first_word(s: &str) -> &str { // Lifetimes inferred
&s[..1]
}
// Static lifetime
let s: &'static str = "I have a static lifetime";// Enums with data
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}
impl Message {
fn call(&self) {
match self {
Message::Quit => println!("Quit"),
Message::Move { x, y } => println!("Move to {}, {}", x, y),
Message::Write(text) => println!("Text: {}", text),
Message::ChangeColor(r, g, b) => println!("RGB: {}, {}, {}", r, g, b),
}
}
}
// Option<T>
fn divide(numerator: f64, denominator: f64) -> Option<f64> {
if denominator == 0.0 {
None
} else {
Some(numerator / denominator)
}
}
// Pattern matching with Option
match divide(10.0, 2.0) {
Some(result) => println!("Result: {}", result),
None => println!("Cannot divide by zero"),
}
// if let syntax
if let Some(result) = divide(10.0, 2.0) {
println!("Result: {}", result);
}
// Result<T, E> for error handling
fn read_username_from_file() -> Result<String, std::io::Error> {
let mut file = std::fs::File::open("username.txt")?;
let mut username = String::new();
file.read_to_string(&mut username)?;
Ok(username)
}// Define trait
trait Summary {
fn summarize(&self) -> String;
// Default implementation
fn summarize_author(&self) -> String {
String::from("Unknown")
}
}
// Implement trait
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 {
self.author.clone()
}
}
// Trait bounds
fn notify<T: Summary>(item: &T) {
println!("Breaking news! {}", item.summarize());
}
// Multiple trait bounds
fn notify_display<T: Summary + std::fmt::Display>(item: &T) {
println!("{}: {}", item, item.summarize());
}
// Where clause for readability
fn some_function<T, U>(t: &T, u: &U) -> i32
where
T: std::fmt::Display + Clone,
U: Clone + std::fmt::Debug,
{
// Implementation
42
}
// Return types implementing traits
fn returns_summarizable() -> impl Summary {
Article {
headline: String::from("News"),
content: String::from("Content here"),
author: String::from("Alice"),
}
}// Generic structs
struct Point<T> {
x: T,
y: T,
}
impl<T> Point<T> {
fn x(&self) -> &T {
&self.x
}
}
// Specific implementations
impl Point<f32> {
fn distance_from_origin(&self) -> f32 {
(self.x.powi(2) + self.y.powi(2)).sqrt()
}
}
// Generic enums
enum Option<T> {
Some(T),
None,
}
enum Result<T, E> {
Ok(T),
Err(E),
}
// Generic functions
fn largest<T: PartialOrd>(list: &[T]) -> &T {
let mut largest = &list[0];
for item in list {
if item > largest {
largest = item;
}
}
largest
}use std::fs::File;
use std::io::{self, Read};
// Using ? operator
fn read_username() -> Result<String, io::Error> {
let mut file = File::open("username.txt")?;
let mut username = String::new();
file.read_to_string(&mut username)?;
Ok(username)
}
// Chaining with ?
fn read_username_short() -> Result<String, io::Error> {
let mut username = String::new();
File::open("username.txt")?.read_to_string(&mut username)?;
Ok(username)
}
// Even shorter
fn read_username_shortest() -> Result<String, io::Error> {
std::fs::read_to_string("username.txt")
}
// Custom error types
use std::fmt;
#[derive(Debug)]
enum MyError {
Io(io::Error),
Parse(std::num::ParseIntError),
Custom(String),
}
impl fmt::Display for MyError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
MyError::Io(err) => write!(f, "IO error: {}", err),
MyError::Parse(err) => write!(f, "Parse error: {}", err),
MyError::Custom(msg) => write!(f, "Error: {}", msg),
}
}
}
impl std::error::Error for MyError {}
impl From<io::Error> for MyError {
fn from(err: io::Error) -> Self {
MyError::Io(err)
}
}
impl From<std::num::ParseIntError> for MyError {
fn from(err: std::num::ParseIntError) -> Self {
MyError::Parse(err)
}
}
// Using custom error
fn process_file(path: &str) -> Result<i32, MyError> {
let content = std::fs::read_to_string(path)?;
let number: i32 = content.trim().parse()?;
Ok(number * 2)
}// anyhow for applications
use anyhow::{Context, Result};
fn read_config() -> Result<String> {
std::fs::read_to_string("config.toml")
.context("Failed to read config file")
}
// thiserror for libraries
use thiserror::Error;
#[derive(Error, Debug)]
pub enum DataError {
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
#[error("Invalid data at line {line}")]
InvalidData { line: usize },
#[error("Missing field: {0}")]
MissingField(String),
}use tokio;
// Async function
async fn fetch_url(url: &str) -> Result<String, reqwest::Error> {
let response = reqwest::get(url).await?;
let body = response.text().await?;
Ok(body)
}
// Async main with tokio
#[tokio::main]
async fn main() {
match fetch_url("https://example.com").await {
Ok(body) => println!("Body: {}", body),
Err(e) => eprintln!("Error: {}", e),
}
}
// Multiple concurrent tasks
async fn fetch_multiple() -> Result<(), Box<dyn std::error::Error>> {
let (result1, result2, result3) = tokio::join!(
fetch_url("https://example.com"),
fetch_url("https://example.org"),
fetch_url("https://example.net"),
);
println!("Result 1: {:?}", result1);
println!("Result 2: {:?}", result2);
println!("Result 3: {:?}", result3);
Ok(())
}
// Select (race)
use tokio::time::{sleep, Duration};
async fn race_example() {
tokio::select! {
_ = sleep(Duration::from_secs(1)) => {
println!("Timeout!");
}
result = fetch_url("https://example.com") => {
println!("Fetch completed: {:?}", result);
}
}
}
// Spawn tasks
async fn spawn_tasks() {
let handle1 = tokio::spawn(async {
// Do work
42
});
let handle2 = tokio::spawn(async {
// Do more work
100
});
let result1 = handle1.await.unwrap();
let result2 = handle2.await.unwrap();
println!("Results: {}, {}", result1, result2);
}use tokio::sync::{mpsc, oneshot};
// Multiple producer, single consumer
async fn mpsc_example() {
let (tx, mut rx) = mpsc::channel(32);
// Spawn producers
for i in 0..10 {
let tx = tx.clone();
tokio::spawn(async move {
tx.send(i).await.unwrap();
});
}
// Drop original sender to close channel
drop(tx);
// Receive messages
while let Some(msg) = rx.recv().await {
println!("Received: {}", msg);
}
}
// One-shot channel
async fn oneshot_example() {
let (tx, rx) = oneshot::channel();
tokio::spawn(async move {
tx.send(42).unwrap();
});
let value = rx.await.unwrap();
println!("Value: {}", value);
}use axum::{
extract::{Path, Query, State},
http::StatusCode,
response::Json,
routing::{get, post},
Router,
};
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use tokio::sync::RwLock;
#[derive(Clone)]
struct AppState {
users: Arc<RwLock<Vec<User>>>,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
struct User {
id: u64,
name: String,
email: String,
}
#[derive(Deserialize)]
struct CreateUser {
name: String,
email: String,
}
#[derive(Deserialize)]
struct Pagination {
page: Option<usize>,
per_page: Option<usize>,
}
// Handlers
async fn get_users(
State(state): State<AppState>,
Query(pagination): Query<Pagination>,
) -> Json<Vec<User>> {
let users = state.users.read().await;
let page = pagination.page.unwrap_or(0);
let per_page = pagination.per_page.unwrap_or(10);
let start = page * per_page;
let end = (start + per_page).min(users.len());
Json(users[start..end].to_vec())
}
async fn get_user(
State(state): State<AppState>,
Path(id): Path<u64>,
) -> Result<Json<User>, StatusCode> {
let users = state.users.read().await;
users
.iter()
.find(|u| u.id == id)
.cloned()
.map(Json)
.ok_or(StatusCode::NOT_FOUND)
}
async fn create_user(
State(state): State<AppState>,
Json(payload): Json<CreateUser>,
) -> (StatusCode, Json<User>) {
let mut users = state.users.write().await;
let id = users.len() as u64 + 1;
let user = User {
id,
name: payload.name,
email: payload.email,
};
users.push(user.clone());
(StatusCode::CREATED, Json(user))
}
#[tokio::main]
async fn main() {
let state = AppState {
users: Arc::new(RwLock::new(vec![
User {
id: 1,
name: "Alice".to_string(),
email: "alice@example.com".to_string(),
},
])),
};
let app = Router::new()
.route("/users", get(get_users).post(create_user))
.route("/users/:id", get(get_user))
.with_state(state);
let listener = tokio::net::TcpListener::bind("127.0.0.1:3000")
.await
.unwrap();
println!("Server running on http://127.0.0.1:3000");
axum::serve(listener, app).await.unwrap();
}// Tests in same file
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_add() {
assert_eq!(add(2, 2), 4);
}
#[test]
fn test_divide() {
assert_eq!(divide(10.0, 2.0), Some(5.0));
assert_eq!(divide(10.0, 0.0), None);
}
#[test]
#[should_panic(expected = "divide by zero")]
fn test_divide_panic() {
divide_panic(10.0, 0.0);
}
#[test]
fn test_result() -> Result<(), String> {
if 2 + 2 == 4 {
Ok(())
} else {
Err(String::from("Math is broken"))
}
}
}
// Property-based testing with proptest
#[cfg(test)]
mod proptests {
use proptest::prelude::*;
proptest! {
#[test]
fn test_reverse_twice(ref s in "\\PC*") {
let reversed_once: String = s.chars().rev().collect();
let reversed_twice: String = reversed_once.chars().rev().collect();
assert_eq!(s, &reversed_twice);
}
#[test]
fn test_add_commutative(a in 0..1000i32, b in 0..1000i32) {
assert_eq!(a + b, b + a);
}
}
}// tests/integration_test.rs
use my_crate::User;
#[test]
fn test_user_creation() {
let user = User::new("Alice", "alice@example.com");
assert_eq!(user.name, "Alice");
assert_eq!(user.email, "alice@example.com");
}
// Async tests
#[tokio::test]
async fn test_async_function() {
let result = fetch_data().await;
assert!(result.is_ok());
}// benches/benchmark.rs
use criterion::{black_box, criterion_group, criterion_main, Criterion};
fn fibonacci(n: u64) -> u64 {
match n {
0 => 1,
1 => 1,
n => fibonacci(n - 1) + fibonacci(n - 2),
}
}
fn criterion_benchmark(c: &mut Criterion) {
c.bench_function("fib 20", |b| b.iter(|| fibonacci(black_box(20))));
}
criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);// Prefer iterators over loops
let sum: i32 = vec![1, 2, 3, 4, 5]
.iter()
.map(|x| x * 2)
.filter(|x| x > &5)
.sum();
// Use match for exhaustive handling
match result {
Ok(value) => println!("Success: {}", value),
Err(e) => eprintln!("Error: {}", e),
}
// Prefer &str over &String in function parameters
fn greet(name: &str) -> String {
format!("Hello, {}!", name)
}// Bad - unnecessary clone
fn process(data: &Vec<i32>) -> Vec<i32> {
data.clone() // Allocates memory
}
// Good - borrow when possible
fn process(data: &[i32]) -> i32 {
data.iter().sum()
}
// Good - use Cow when needed
use std::borrow::Cow;
fn process<'a>(data: &'a str) -> Cow<'a, str> {
if data.contains("bad") {
Cow::Owned(data.replace("bad", "good"))
} else {
Cow::Borrowed(data)
}
}// Newtype pattern for type safety
struct UserId(u64);
struct ProductId(u64);
fn get_user(id: UserId) -> User {
// Cannot accidentally pass ProductId
}
// Builder pattern with typestate
struct Locked;
struct Unlocked;
struct Door<State> {
state: PhantomData<State>,
}
impl Door<Locked> {
fn unlock(self) -> Door<Unlocked> {
Door { state: PhantomData }
}
}
impl Door<Unlocked> {
fn lock(self) -> Door<Locked> {
Door { state: PhantomData }
}
fn open(&self) {
println!("Opening door");
}
}// Use Result<T, E> for recoverable errors
fn parse_config(path: &str) -> Result<Config, ConfigError> {
// Implementation
}
// Use panic! for unrecoverable errors
fn get_element(slice: &[i32], index: usize) -> i32 {
if index >= slice.len() {
panic!("Index out of bounds");
}
slice[index]
}
// Use Option<T> for nullable values
fn find_user(id: u64) -> Option<User> {
// Implementation
}# Cargo.toml
[dependencies]
serde = { version = "1.0", features = ["derive"] }
tokio = { version = "1", features = ["full"] }
[dev-dependencies]
criterion = "0.5"
[profile.release]
opt-level = 3
lto = true
codegen-units = 1/// Divides two numbers
///
/// # Arguments
///
/// * `numerator` - The number to be divided
/// * `denominator` - The number to divide by
///
/// # Returns
///
/// * `Some(f64)` - The result of division
/// * `None` - If denominator is zero
///
/// # Examples
///
/// ```
/// let result = divide(10.0, 2.0);
/// assert_eq!(result, Some(5.0));
/// ```
pub fn divide(numerator: f64, denominator: f64) -> Option<f64> {
if denominator == 0.0 {
None
} else {
Some(numerator / denominator)
}
}#[derive(Default)]
struct User {
name: String,
email: String,
age: Option<u32>,
}
struct UserBuilder {
user: User,
}
impl UserBuilder {
fn new() -> Self {
Self {
user: User::default(),
}
}
fn name(mut self, name: impl Into<String>) -> Self {
self.user.name = name.into();
self
}
fn email(mut self, email: impl Into<String>) -> Self {
self.user.email = email.into();
self
}
fn age(mut self, age: u32) -> Self {
self.user.age = Some(age);
self
}
fn build(self) -> User {
self.user
}
}
// Usage
let user = UserBuilder::new()
.name("Alice")
.email("alice@example.com")
.age(30)
.build();struct File {
handle: std::fs::File,
}
impl File {
fn new(path: &str) -> std::io::Result<Self> {
let handle = std::fs::File::open(path)?;
Ok(Self { handle })
}
}
impl Drop for File {
fn drop(&mut self) {
println!("Closing file");
// File automatically closed
}
}// Bad - trying to hold multiple mutable references
let mut data = vec![1, 2, 3];
let first = &mut data[0];
let second = &mut data[1]; // ERROR
// Good - use split_at_mut or indices
let mut data = vec![1, 2, 3];
let (left, right) = data.split_at_mut(1);
left[0] = 10;
right[0] = 20;// Bad
fn greet(name: String) -> String {
format!("Hello, {}", name)
}
// Good
fn greet(name: &str) -> String {
format!("Hello, {}", name)
}// Bad
let value = some_option.unwrap();
// Good
let value = some_option.expect("Value should exist");
// Better
let value = match some_option {
Some(v) => v,
None => return Err(Error::MissingValue),
};# Create new project
cargo new my_project
cargo new --lib my_library
# Build and run
cargo build
cargo run
cargo build --release
# Testing
cargo test
cargo test --test integration_test
cargo test -- --nocapture
# Documentation
cargo doc --open
# Linting
cargo clippy
cargo clippy -- -D warnings
# Formatting
cargo fmt
cargo fmt --check
# Dependencies
cargo add tokio
cargo update
cargo tree