skills/rust/SKILL.md
Use when editing Rust files, .rs, Cargo.toml, Cargo.lock, workspaces, async code, error handling, PyO3, maturin, napi-rs, C ABI, platform support, tests, or performance-critical Rust paths.
npx skillsauth add cofin/flow rustInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Patterns for multi-crate Rust workspaces targeting cross-platform, high-performance systems with polyglot extension surfaces. Covers workspace layout, async runtimes, platform abstraction, PyO3/maturin Python bindings, napi-rs Node/Bun bindings, C ABI/FFI, error handling, and benchmarking.
Cargo.toml:[workspace.lints.rust]
unexpected_cfgs = { level = "allow", check-cfg = ['cfg(Py_GIL_DISABLED)'] }
[workspace.lints.clippy]
too_many_arguments = "allow"
type_complexity = "allow"
[lints] workspace = true.cargo fmt. Lint: cargo clippy -- -D warnings.tracing (not log) for structured instrumentation./// doc comments.Arc<T> over Rc<T> in async contexts.project/
├── Cargo.toml # [workspace] root
├── crates/
│ ├── core/ # Pure logic, no FFI deps
│ ├── http/ # Runtime + networking (binary)
│ ├── py/ # PyO3 bindings (cdylib)
│ └── node/ # napi-rs bindings
└── rust-toolchain.toml
Core crate has zero FFI dependencies. Binding crates wrap it. Pin shared dependencies in workspace root with [workspace.dependencies]; crates reference with { workspace = true }.
use thiserror::Error;
#[derive(Debug, Error)]
pub enum AppError {
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
#[error("parse error in {path}: {message}")]
Parse { path: String, message: String },
#[error("not found: {0}")]
NotFound(String),
}
pub type Result<T> = std::result::Result<T, AppError>;
#[tokio::main] for binaries; pass runtime handle to libraries."full".Arc<T> for shared state across tasks, never Rc<T>.tokio::sync::Mutex only when holding the lock across .await; otherwise use parking_lot::Mutex.use pyo3::prelude::*;
#[pyclass(frozen)] // frozen = immutable, safe across threads
#[derive(Clone, Debug)]
pub struct Config {
#[pyo3(get)]
pub name: String,
#[pyo3(get)]
pub max_retries: u32,
}
#[pymodule]
#[pyo3(name = "_native")]
pub fn pymodule_init(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_class::<Config>()?;
Ok(())
}
<workflow>
Create a workspace with resolver = "2". Separate pure-logic core from binding crates (py, node, c_abi). Pin all shared dependencies in [workspace.dependencies].
Define per-crate error enums with thiserror. Use #[from] for automatic conversion. Add PyErr conversion (From<AppError> for PyErr) in binding crates.
Write business logic in the core crate with no FFI dependencies. Use async for I/O-bound work. Test with cargo test and benchmark hot paths with criterion.
Wrap core types/functions in binding crates. For PyO3: use #[pyclass(frozen)] for immutable data, future_into_py for async. For napi-rs: use #[napi] macros.
Run cargo clippy -- -D warnings, cargo fmt --check, and cargo test --workspace. For PyO3: maturin develop and run Python tests.
Arc over Rc in async code -- Rc is not Send and will fail to compile in tokio tasks. Use Arc<T> for shared ownership across tasks.thiserror for library error types -- provides #[derive(Error)] with Display and From impls. Reserve anyhow for binaries/scripts only.#[pyclass(frozen)] for immutable data -- enables safe sharing across Python threads without per-access locking.tracing over log -- structured instrumentation with spans, levels, and subscriber flexibility.rust-toolchain.toml -- ensures consistent compiler version across CI and local builds.Before delivering Rust code, verify:
resolver = "2" and [workspace.dependencies]thiserror with #[from] conversionsArc<T> (not Rc<T>) for shared statecargo clippy -- -D warnings passes/// doc commentsrust-toolchain.toml is present and pinnedTask: Error type and async function with proper error handling.
// crates/core/src/error.rs
use thiserror::Error;
#[derive(Debug, Error)]
pub enum StorageError {
#[error("object not found: {key}")]
NotFound { key: String },
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
#[error("serialization error: {0}")]
Serde(#[from] serde_json::Error),
#[error("connection timeout after {elapsed_ms}ms")]
Timeout { elapsed_ms: u64 },
}
pub type Result<T> = std::result::Result<T, StorageError>;
// crates/core/src/store.rs
use std::sync::Arc;
use tokio::fs;
use crate::error::{Result, StorageError};
pub struct ObjectStore {
base_path: Arc<str>,
}
impl ObjectStore {
pub fn new(base_path: impl Into<Arc<str>>) -> Self {
Self { base_path: base_path.into() }
}
/// Read an object by key, returning its bytes.
pub async fn get(&self, key: &str) -> Result<Vec<u8>> {
let path = format!("{}/{}", self.base_path, key);
fs::read(&path).await.map_err(|e| match e.kind() {
std::io::ErrorKind::NotFound => StorageError::NotFound {
key: key.to_string(),
},
_ => StorageError::Io(e),
})
}
/// Write bytes to an object key.
pub async fn put(&self, key: &str, data: &[u8]) -> Result<()> {
let path = format!("{}/{}", self.base_path, key);
if let Some(parent) = std::path::Path::new(&path).parent() {
fs::create_dir_all(parent).await?;
}
fs::write(&path, data).await?;
Ok(())
}
}
</example>
For detailed guides and code examples, refer to the following documents in references/:
testing
Use when syncing Beads state to markdown, checking Flow status, refreshing context docs, validating task markers, or reporting ready/blocked Flow work.
testing
Use when initializing Flow in a repo, configuring .agents, installing or checking Beads bd, setting local-only sync policy, or creating first project context files.
data-ai
Use when drafting PRDs, researching, planning, refining, revising, or creating .agents/specs/<flow_id>/spec.md worksheets for Flow.
testing
Use when implementing Flow tasks from Beads or spec.md, claiming ready work, applying TDD, recording task notes, committing, and syncing after task state changes.