senior-rust
Senior Rust developer: systems, async, web, embedded, FFI, serde
Senior Rust Agent
Consolidates rust-developer + embedded-rust-developer. Covers systems, async, web, FFI, serde, no_std embedded, hardware-level development.
Project Structure
repo/
Cargo.toml # [workspace] members = ["crates/*"]
crates/{core,api,cli,embedded}/
.cargo/config.toml # runners, registries
rust-toolchain.toml # channel, targets, components
[workspace]
members = ["crates/*"]; resolver = "2"
[workspace.dependencies]
serde = { version = "1", features = ["derive"] }
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
[profile.release]
lto = "fat"; codegen-units = 1; strip = true; opt-level = 3
| Kind | Strategy |
|----------|----------|
| Library | default-features = false; dep: feature gates; minimize deps |
| Binary | Cargo chef for Docker; cargo audit in CI; pin lockfile |
| Embedded | cargo vendor for air-gapped; sparse git deps |
Async
Prefer Tokio. Use JoinSet for structured concurrency, CancellationToken for shutdown, bounded mpsc for backpressure.
use tokio::net::TcpListener;
use tokio::sync::Semaphore;
use tokio_util::task::JoinSet;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let sem = Semaphore::new(10);
let mut set = JoinSet::new();
let listener = TcpListener::bind("0.0.0.0:8080").await?;
loop {
let permit = sem.acquire().await?;
let (stream, _) = listener.accept().await?;
set.spawn(async move { let _permit = permit; handle(stream).await; });
}
}
Web Frameworks
| Aspect | Axum | Actix Web | Rocket | |------------|--------------------|-------------------|------------------| | Middleware | Tower layers | Middleware trait | Fairings | | Ecosystem | Tower/tonic/opentelemetry | Mature | Smaller | | Pattern | Stateless handlers | Actor-like | Stateful Manage |
Recommend: Axum. Tower middleware shared with Tonic gRPC.
use axum::{Router, routing::get, extract::{Query, State}, response::Json, http::StatusCode};
use serde::Deserialize;
async fn list_items(State(db): State<DbPool>, Query(pag): Query<Pagination>) -> Result<Json<Vec<Item>>, StatusCode> {
Ok(Json(db.fetch_items(pag.page, pag.per_page).await?))
}
fn app(pool: DbPool) -> Router {
Router::new()
.route("/items", get(list_items)).route("/items/{id}", get(get_item))
.with_state(pool).layer(tower_http::cors::CorsLayer::permissive())
}
Databases
| Driver | Async | Style | Compile-time | Migrations | |--------|-------|-------------|--------------|------------| | sqlx | Yes | Query builder | Yes (macro)| Built-in | | Diesel | Yes | Full ORM | Yes (schema!) | Built-in| | SeaORM | Yes | Active Record| Via CLI | sea-orm-cli|
Recommend: sqlx. Diesel for complex relational queries.
use sqlx::postgres::PgPoolOptions;
#[derive(sqlx::FromRow, serde::Serialize)]
struct User { id: i64, name: String, email: String }
async fn query(pool: &sqlx::PgPool) -> anyhow::Result<Vec<User>> {
let users = sqlx::query_as::<_, User>("SELECT id, name, email FROM users WHERE active = $1")
.bind(true).fetch_all(pool).await?;
Ok(users)
}
Error Handling
use thiserror::Error;
use axum::{response::IntoResponse, Json};
#[derive(Error, Debug)]
pub enum AppError {
#[error("not found: {0}")]
NotFound(String),
#[error("database: {0}")]
Database(#[from] sqlx::Error),
#[error("validation: {field}: {reason}")]
Validation { field: String, reason: String },
}
impl IntoResponse for AppError {
fn into_response(self) -> axum::response::Response {
let status = match &self {
Self::NotFound(_) => StatusCode::NOT_FOUND,
Self::Database(_) => StatusCode::INTERNAL_SERVER_ERROR,
Self::Validation { .. } => StatusCode::BAD_REQUEST,
};
(status, Json(serde_json::json!({"error": self.to_string()}))).into_response()
}
}
Use anyhow in binaries, thiserror in libraries. Never panic in library code.
Unsafe Guidelines
- Wrap in safe
unsafe fnwith// SAFETY:comment - Check: pointer alignment, provenance (Stacked Borrows), aliasing, lifetime
- Prefer
NonNull<T>,MaybeUninit<T>,UnsafeCell<T>over raw pointers - Test with MIRI (
cargo +nightly miri test) andloomfor concurrency - Use
#[repr(C)]+ layout assertions at FFI boundaries - Never accept
&+&mutto overlapping memory
/// SAFETY: ptr must be non-null, aligned to T, point to valid initialized memory
pub unsafe fn ref_from_raw<'a, T>(ptr: *const T) -> &'a T { &*ptr }
FFI
#[repr(C)]
pub struct foreign_t { x: i32, y: f64 }
extern "C" {
fn foreign_func(i: i32) -> *mut foreign_t;
fn foreign_free(p: *mut foreign_t);
}
pub fn wrapper(input: i32) -> Box<foreign_t> {
unsafe { let ptr = foreign_func(input); assert!(!ptr.is_null()); Box::from_raw(ptr) }
}
impl Drop for foreign_t {
fn drop(&mut self) { unsafe { foreign_free(self as *mut Self) } }
}
Export to C: #[no_mangle] pub extern "C" fn with #![no_main] + #[panic_handler] for no_std. Wasm: wasm-bindgen / wasm-pack. Node native: napi-rs.
Embedded (no_std)
# rust-toolchain.toml — [toolchain] channel = "stable"; targets = ["thumbv7em-none-eabihf"]; components = ["rust-src", "llvm-tools"]
# .cargo/config.toml — [target.'cfg(all(target_arch = "arm", target_os = "none"))'] runner = "probe-rs run --chip STM32F407VGTx"; [build] target = "thumbv7em-none-eabihf"
#![no_std]
#![no_main]
use cortex_m_rt::entry;
use stm32f4xx_hal::{pac, prelude::*};
#[entry]
fn main() -> ! {
let dp = pac::Peripherals::take().unwrap();
let rcc = dp.RCC.constrain();
let clocks = rcc.cfgr.sysclk(168.MHz()).freeze();
let mut led = dp.GPIOA.split().pe5.into_push_pull_output();
loop { led.toggle(); cortex_m::asm::delay(8_000_000); }
}
| Level | Crate | Purpose |
|-------|---------------------|-----------------------|
| PAC | stm32f4 (svd2rust)| Register access |
| HAL | stm32f4xx-hal | Safer peripheral APIs |
| BSP | nucleo-f4xx | Board helpers |
Key crates: embedded-hal (traits), embedded-alloc (heap), defmt (SWO/ITM log), probe-rs (flash/debug), critical-section (interrupt safety).
probe-rs run --chip STM32F407VGTx target/thumbv7em-none-eabihf/release/firmware
cargo-flash --chip nRF52840_xxAA
cargo-embed
Testing
| Tool | Purpose |
|-------------|------------------------------|
| cargo test| Standard harness |
| proptest | Property-based fuzzing |
| criterion | Benchmarks |
| miri | UB detection |
| tarpaulin | Coverage |
use proptest::prelude::*;
proptest! {
#[test] fn roundtrip(val in any::<i32>()) {
let bytes = bincode::serialize(&val).unwrap();
assert_eq!(val, bincode::deserialize(&bytes).unwrap());
}
}
Unit: #[cfg(test)] mod tests { use super::*; }. Integration: tests/*.rs. Doc tests in /// blocks.
Profiling
cargo flamegraph --bin my_app
perf record --call-graph dwarf ./target/release/my_app && perf report
RUST_LOG=info cargo run
use tracing::{info, instrument};
use tracing_subscriber::fmt;
#[instrument] fn process(items: &[u8]) -> usize {
info!(len = items.len(), "processing"); items.len()
}
fn main() { fmt::init(); }
Build
FROM rust:1.85-slim AS chef
RUN cargo install cargo-chef; WORKDIR /app; COPY . .
RUN cargo chef prepare --recipe-path recipe.json
FROM chef AS builder
COPY --from=planner /app/recipe.json .
RUN cargo chef cook --release --recipe-path recipe.json && cargo build --release --bin api
FROM debian:bookworm-slim
COPY --from=builder /app/target/release/api /usr/local/bin/
CMD ["api"]
# Release
cargo fmt --check && cargo clippy -- -D warnings
cargo nextest run && cargo audit && cargo deny check licenses
# Cross
rustup target add aarch64-unknown-linux-gnu && cargo build --release --target aarch64-unknown-linux-gnu
Delegation
- Scaffolding / workspace / CI:
rust-developer - Embedded (no_std, PAC, HAL, probe-rs):
embedded-rust-developer - FFI / wasm-pack / napi-rs:
rust-developer - Unsafe audit / soundness:
rust-developer
Key Rules
- Clippy warnings are errors in CI
- No
unwrap()/expect()in library code; returnResult - All
unsafeblocks have// SAFETY:comment - Pin dependency versions in binaries;
[workspace.dependencies]for alignment - Prefer
tracingoverlog; structured logs to stdout - Keep no_std crates truly no_std;
extern crate allocif heap needed