Install
openclaw skills install @deepseekoracle/rust-advancedAdvanced Rust on Windows, macOS, and Linux: ownership and lifetimes, Result/error design, async Tokio, concurrency, serde/clap/tracing, Cargo features and profiles, testing, profiling, cross-compilation, Windows FFI, and PyO3/maturin. Use when the borrow checker, an async Send error, a linker error, a version conflict, or a production binary needs a real diagnosis.
openclaw skills install @deepseekoracle/rust-advancedAudited toolchain: rustc 1.93.1, cargo 1.93.1, rustup installed. A cargo new + cargo run smoke test returned Hello, world! (exit 0).
Rust's compiler is a design partner, not an obstacle. Most fights come from one of these:
Result<T, E> is normal control flow.When stuck, ask:
rustup show
rustup update stable
rustup component add rustfmt clippy rust-src
rustup target list --installed
cargo --version
New project:
cargo new app --bin
cargo new libname --lib
cargo add serde --features derive
cargo add tokio --features full
cargo add anyhow thiserror
Workspace:
[workspace]
members = ["app", "core", "cli"]
resolver = "2"
Cargo.toml baseline:
[package]
name = "app"
version = "0.1.0"
edition = "2024"
rust-version = "1.85"
[dependencies]
anyhow = "1"
thiserror = "2"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
clap = { version = "4", features = ["derive"] }
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
[profile.release]
opt-level = 3
lto = "thin"
codegen-units = 1
strip = "symbols"
panic = "abort"
Use edition = "2024" only if the toolchain supports it; otherwise pin 2021.
fn takes_owned(s: String) { println!("{s}"); }
fn borrows(s: &str) -> usize { s.len() }
fn main() {
let s = String::from("hello");
let n = borrows(&s); // borrow ends here
takes_owned(s); // move is now allowed
println!("{n}");
}
Rules that fix most errors:
String/Vec<T> when the caller must own the data.&str/&[T] when you only read.clone() deliberately at boundaries, not to silence every error.Cow<'a, str> for optionally borrowed strings.Arc<T> for shared ownership across threads; Rc<T> is single-thread only.RefCell<T> for interior mutability in single-thread code; Mutex/RwLock across threads.Lifetime template:
struct Parser<'a> {
input: &'a str,
}
impl<'a> Parser<'a> {
fn new(input: &'a str) -> Self { Self { input } }
fn rest(&self) -> &'a str { self.input }
}
If a lifetime is hard, first try owning the data; optimize to borrow later.
Use Result for recoverable errors; panic! only for bugs/invariants.
use thiserror::Error;
#[derive(Debug, Error)]
pub enum AppError {
#[error("io: {0}")]
Io(#[from] std::io::Error),
#[error("bad config: {0}")]
Config(String),
}
pub fn load(path: &str) -> Result<String, AppError> {
let s = std::fs::read_to_string(path)?;
if s.is_empty() { return Err(AppError::Config("empty".into())); }
Ok(s)
}
thiserror enum, no anyhow in public API.anyhow::Result<T> at the top; add context with .context("...")?..unwrap() on user input, files, network, or env vars.? converts via From; implement From or use #[from].#[tokio::main]
async fn main() -> anyhow::Result<()> {
let a = tokio::spawn(async { 1 + 1 });
let b = tokio::spawn(async { 2 + 2 });
println!("{} {}", a.await?, b.await?);
Ok(())
}
Rules:
spawn_blocking.std::thread::sleep, synchronous file I/O, or a long CPU loop.tokio::time::timeout around network calls.tokio::select! for cancellation/racing.Arc<Mutex<T>> or channels for shared state; prefer message passing.Mutex guard cannot be held across .await unless using tokio::sync::Mutex.Send errors usually mean an Rc, RefCell, or non-Send guard is held across await.Threads:
use std::sync::{Arc, Mutex};
let state = Arc::new(Mutex::new(0));
let s2 = Arc::clone(&state);
std::thread::spawn(move || *s2.lock().unwrap() += 1);
Use crossbeam-channel or std::sync::mpsc; for async use tokio::sync::mpsc.
use clap::Parser;
use serde::{Deserialize, Serialize};
#[derive(Debug, Parser)]
struct Cli {
#[arg(long)]
input: String,
#[arg(long, default_value = "out.json")]
out: String,
}
#[derive(Debug, Serialize, Deserialize)]
struct Record { id: u64, name: String }
#[serde(rename_all = "camelCase")] for API boundaries.#[serde(default)] for optional fields.serde_json::from_str / to_string_pretty.tracing_subscriber::fmt().with_env_filter(...).init().cargo fmt --check
cargo clippy --all-targets --all-features -- -D warnings
cargo test
cargo test -- --nocapture
cargo doc --no-deps --open
Unit test:
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn adds() { assert_eq!(2 + 2, 4); }
}
Integration test in tests/:
#[test]
fn cli_runs() {
let out = std::process::Command::new(env!("CARGO_BIN_EXE_app"))
.arg("--help").output().unwrap();
assert!(out.status.success());
}
Add cargo-audit / cargo-deny for dependency security; cargo nextest for faster test runs if installed.
cargo flamegraph, perf, or samply.codegen-units=1, strip, panic=abort where acceptable.cargo build --release; use --target for cross builds.cargo bloat to find binary weight.clone; measure allocations.Vec::with_capacity, iterators, and &str at boundaries.#[inline] only after measurement.[dependencies]
windows = { version = "0.62", features = ["Win32_Foundation", "Win32_System_Threading"] }
#[cfg(windows)]
fn pid() -> u32 { std::process::id() }
windows-rs over hand-written FFI when available.unsafe blocks need a safety comment: preconditions, ownership, lifetime.extern "C" + #[no_mangle] for C ABI; cbindgen for headers.[lib]
name = "fastcore"
crate-type = ["cdylib"]
[dependencies]
pyo3 = { version = "0.26", features = ["extension-module"] }
use pyo3::prelude::*;
#[pyfunction]
fn add(a: i64, b: i64) -> i64 { a + b }
#[pymodule]
fn fastcore(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_function(wrap_pyfunction!(add, m)?)?;
Ok(())
}
python -m pip install maturin
maturin develop --release
This template is not compiled here; pin versions to the current PyO3 docs.
| Symptom | Cause | Fix |
|---|---|---|
cannot move out of borrowed content | trying to move from &T | clone, take ownership, or use Option::take |
does not live long enough | reference outlives owner | return owned data; narrow scope; add lifetime only if correct |
cannot borrow as mutable | overlapping borrows | shorten borrow; split fields; use RefCell/Mutex deliberately |
future is not Send | Rc/RefCell/guard across .await | use Arc/tokio::sync; drop guard before await |
| async task hangs | blocking call in executor | spawn_blocking or async I/O |
linker link.exe not found | MSVC build tools missing | install VS Build Tools C++ workload |
| OpenSSL link error | native OpenSSL | use rustls or install OpenSSL |
| target not installed | missing std | rustup target add <triple> |
| duplicate crate versions | semver conflict | cargo tree -d; align versions |
| proc macro compile error | stale expansion | cargo clean; update macro crate |
| binary huge | debug info/LTO | release profile + strip + LTO |
| tests pass, prod fails | env/config difference | integration test; log config keys, not secrets |
cargo slow | rebuild everything | sccache, narrower features, workspace |
| Windows path/encoding | non-UTF-8 | use OsStr, PathBuf; avoid String assumptions |
templates/cli.rs — clap + serde JSON + anyhow.templates/error.rs — thiserror enum + ?.templates/async.rs — Tokio timeout + spawn.templates/lib.rs — library API + tests.See references/SOURCES.md.