

Also from Kynth Studios


Also from Kynth Studios


Also from Kynth Studios
1234567# Rust Best Practices89## Error Handling1011### Application Code (anyhow)1213For application code, use **anyhow** for error handling:1415```rust16use anyhow::{Context, Result};1718fn process_data(path: &str) -> Result<()> {19 let content = std::fs::read_to_string(path)20 .with_context(|| format!("Failed to read file: {}", path))?;2122 // Process content...23 Ok(())24}25```2627### Library Code (thiserror)2829For library code, use **thiserror** to define custom error types:3031```rust32use thiserror::Error;3334#[derive(Error, Debug)]35pub enum MyError {36 #[error("Invalid input: {0}")]37 InvalidInput(String),3839 #[error("IO error: {0}")]40 Io(#[from] std::io::Error),4142 #[error("Parse error: {0}")]43 Parse(#[from] serde_json::Error),44}45```4647**Rules:**4849- ✅ Use `anyhow::Result<T>` in application code50- ✅ Use `thiserror` for library error types51- ✅ Always use `?` operator for error propagation52- ✅ Add context with `.with_context()` or `.context()` when appropriate53- ❌ Do NOT use `unwrap()` or `expect()` in production code (only in tests or when absolutely certain)5455## Concurrency and Synchronization5657### Channels (mpsc)5859**Always prefer channels** for concurrent communication:6061```rust62use std::sync::mpsc;63use std::thread;6465let (tx, rx) = mpsc::channel();6667thread::spawn(move || {68 tx.send("Hello from thread").unwrap();69});7071let received = rx.recv().unwrap();72```7374For async code, use `tokio::sync::mpsc`:7576```rust77use tokio::sync::mpsc;7879let (mut tx, mut rx) = mpsc::channel(32);8081tokio::spawn(async move {82 tx.send("Hello").await.unwrap();83});8485while let Some(msg) = rx.recv().await {86 // Process message87}88```8990**Rules:**9192- ✅ Prefer `mpsc::channel` for sync code93- ✅ Prefer `tokio::sync::mpsc` for async code94- ✅ Use `Arc<Mutex<T>>` only when shared mutable state is necessary95- ✅ Consider `RwLock` for read-heavy workloads96- ❌ Avoid `unsafe` blocks for synchronization9798## Async Runtime99100### Tokio101102Use **Tokio** as the async runtime:103104```rust105use tokio;106107#[tokio::main]108async fn main() -> Result<()> {109 // Async code here110 Ok(())111}112```113114**Cargo.toml:**115116```toml117[dependencies]118tokio = { version = "1", features = ["full"] }119```120121**Rules:**122123- ✅ Use `tokio` for async runtime124- ✅ Use `#[tokio::main]` for async main functions125- ✅ Prefer async/await over manual Future handling126- ✅ Use `tokio::spawn` for concurrent tasks127- ❌ Do NOT use other async runtimes (async-std, smol) unless specifically required128129## Web and gRPC Frameworks130131### Web: Axum132133Use **Axum** for web applications:134135```rust136use axum::{137 routing::get,138 Router,139 Json,140};141use serde_json::{json, Value};142143async fn handler() -> Json<Value> {144 Json(json!({ "message": "Hello, World!" }))145}146147#[tokio::main]148async fn main() {149 let app = Router::new()150 .route("/", get(handler));151152 let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();153 axum::serve(listener, app).await.unwrap();154}155```156157### gRPC: Tonic158159Use **Tonic** for gRPC services:160161```rust162use tonic::{transport::Server, Request, Response, Status};163164pub mod hello {165 tonic::include_proto!("hello");166}167168#[tokio::main]169async fn main() -> Result<(), Box<dyn std::error::Error>> {170 let addr = "[::1]:50051".parse()?;171 let greeter = MyGreeter::default();172173 Server::builder()174 .add_service(hello::greeter_server::GreeterServer::new(greeter))175 .serve(addr)176 .await?;177178 Ok(())179}180```181182**Rules:**183184- ✅ Use `axum` for HTTP/web applications185- ✅ Use `tonic` for gRPC services186- ✅ Leverage Axum's type-safe routing and extractors187- ❌ Do NOT use other web frameworks (actix-web, warp) unless specifically required188189## Standard Traits190191### Always Implement Standard Conversion Traits192193When converting between types, **always implement** the appropriate standard traits:194195### From / Into196197For infallible conversions:198199```rust200use std::convert::From;201202struct Point {203 x: i32,204 y: i32,205}206207impl From<(i32, i32)> for Point {208 fn from((x, y): (i32, i32)) -> Self {209 Point { x, y }210 }211}212213// Now you can use:214let point: Point = (10, 20).into();215```216217### TryFrom / TryInto218219For fallible conversions:220221```rust222use std::convert::TryFrom;223224struct PositiveNumber(i32);225226impl TryFrom<i32> for PositiveNumber {227 type Error = String;228229 fn try_from(value: i32) -> Result<Self, Self::Error> {230 if value > 0 {231 Ok(PositiveNumber(value))232 } else {233 Err(format!("{} is not positive", value))234 }235 }236}237```238239### FromStr240241For parsing from strings:242243```rust244use std::str::FromStr;245246struct Email(String);247248impl FromStr for Email {249 type Err = String;250251 fn from_str(s: &str) -> Result<Self, Self::Err> {252 if s.contains('@') {253 Ok(Email(s.to_string()))254 } else {255 Err("Invalid email format".to_string())256 }257 }258}259260// Usage:261let email: Email = "user@example.com".parse()?;262```263264### Display / Debug265266Always implement `Display` and `Debug`:267268```rust269use std::fmt;270271struct MyType {272 value: i32,273}274275impl fmt::Display for MyType {276 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {277 write!(f, "MyType({})", self.value)278 }279}280281// Debug is usually derived:282#[derive(Debug)]283struct MyType {284 value: i32,285}286```287288**Rules:**289290- ✅ Always implement `From` / `TryFrom` when converting between types291- ✅ Always implement `FromStr` when parsing from strings292- ✅ Always implement `Display` for user-facing output293- ✅ Always derive or implement `Debug` for all types294- ✅ Prefer `From` over manual conversion functions295- ✅ Use `TryFrom` when conversion can fail296- ❌ Do NOT create custom conversion functions when standard traits apply297298## Safety299300### No Unsafe Code301302**Never use `unsafe` blocks** unless absolutely necessary and well-documented:303304```rust305// ❌ BAD - Avoid unsafe306unsafe {307 let ptr = raw_ptr.as_ref().unwrap();308}309310// ✅ GOOD - Use safe alternatives311if let Some(value) = raw_ptr.as_ref() {312 // Use value safely313}314```315316**Rules:**317318- ✅ Always prefer safe Rust code319- ✅ Use safe abstractions from standard library320- ✅ Use `Option` and `Result` for error handling321- ✅ Use `Arc`, `Mutex`, `RwLock` for shared state322- ❌ Do NOT use `unsafe` blocks323- ❌ Do NOT use raw pointers (`*const T`, `*mut T`)324- ❌ Do NOT use `transmute` or other unsafe operations325- ⚠️ If `unsafe` is absolutely necessary, document why and ensure soundness326327## Code Organization328329### Project Structure330331```332project/333├── Cargo.toml334├── src/335│ ├── main.rs # Binary entry point336│ ├── lib.rs # Library entry point337│ ├── error.rs # Error types (thiserror)338│ ├── config.rs # Configuration339│ ├── handlers/ # Request handlers340│ │ └── mod.rs341│ ├── models/ # Data models342│ │ └── mod.rs343│ ├── services/ # Business logic344│ │ └── mod.rs345│ └── utils/ # Utilities346│ └── mod.rs347└── tests/ # Integration tests348```349350### Module Organization351352```rust353// lib.rs354pub mod error;355pub mod config;356pub mod handlers;357pub mod models;358pub mod services;359360pub use error::{Error, Result};361```362363## Testing364365### Unit Tests366367```rust368#[cfg(test)]369mod tests {370 use super::*;371372 #[test]373 fn test_conversion() {374 let point: Point = (10, 20).into();375 assert_eq!(point.x, 10);376 assert_eq!(point.y, 20);377 }378}379```380381### Integration Tests382383```rust384// tests/integration_test.rs385use my_lib::*;386387#[test]388fn test_api() {389 // Test code390}391```392393## Dependencies394395### Recommended Cargo.toml Structure396397```toml398[package]399name = "my-project"400version = "0.1.0"401edition = "2021"402403[dependencies]404# Error handling405anyhow = "1.0"406thiserror = "1.0"407408# Async runtime409tokio = { version = "1", features = ["full"] }410411# Web framework412axum = "0.8"413414# gRPC415tonic = "0.11"416prost = "0.13"417418# Serialization419serde = { version = "1.0", features = ["derive"] }420serde_json = "1.0"421422# Logging423tracing = "0.1"424tracing-subscriber = "0.3"425426[dev-dependencies]427# Testing428tokio-test = "0.4"429```430431## Summary Checklist432433When writing Rust code, ensure:434435- ✅ Error handling: `anyhow` for apps, `thiserror` for libraries436- ✅ Concurrency: Prefer channels (`mpsc`) over shared state437- ✅ Async: Use `tokio` runtime438- ✅ Web: Use `axum` for HTTP services439- ✅ gRPC: Use `tonic` for gRPC services440- ✅ Traits: Implement `From`, `TryFrom`, `FromStr` for conversions441- ✅ Safety: Never use `unsafe` blocks442- ✅ Testing: Write unit and integration tests443- ✅ Documentation: Document public APIs with doc comments444
One repository carrying more than one format is the comparison this product exists for: does anyone actually write different content in each file, or is one a copy of the other?
| Repository | Format | Stack | Covers | Score | Changed |
|---|---|---|---|---|---|
| tyrchen/geektime-bootcamp-ai.cursor/rules/python-fastapi-backend.mdc · 230 | Cursor rules | setuptestlint-formatstyle+7 | 84/100 | 9 days ago | |
| tyrchen/geektime-bootcamp-ai.cursor/rules/specify-rules.mdc · 230 | Cursor rules | stylearch | 52/100 | 9 days ago | |
| tyrchen/geektime-bootcamp-aisite/CLAUDE.md · 230 | CLAUDE.md | agent-behaviour | 25/100 | 9 days ago | |
| tyrchen/geektime-bootcamp-aiw3/raflow/CLAUDE.md · 230 | CLAUDE.md | agent-behaviour | 25/100 | 9 days ago | |
| tyrchen/geektime-bootcamp-aiw6/codereview-agent/CLAUDE.md · 230 | CLAUDE.md | setupbuildlint-formatarch+4 | 90/100 | 9 days ago | |
| tyrchen/geektime-bootcamp-aiw6/opencode-introspection/CLAUDE.md · 230 | CLAUDE.md | setupbuildtestlint-format+10 | 84/100 | 9 days ago | |
| tyrchen/geektime-bootcamp-aiw6/opencode-introspection/visualizer/CLAUDE.md · 230 | CLAUDE.md | setupbuildtestlint-format+9 | 78/100 | 9 days ago | |
| tyrchen/geektime-bootcamp-aiw6/simple-agent/CLAUDE.md · 230 | CLAUDE.md | setupbuildtestlint-format+10 | 84/100 | 9 days ago | |
| tyrchen/geektime-bootcamp-aiw7/genslides/backend/CLAUDE.md · 230 | CLAUDE.md | testlint-formatstylearch+6 | 100/100 | 9 days ago | |
| tyrchen/geektime-bootcamp-aiw7/genslides/frontend/CLAUDE.md · 230 | CLAUDE.md | teststylearchtypes+5 | 88/100 | 9 days ago | |
| tyrchen/geektime-bootcamp-aiw5/pg-mcp/CLAUDE.md · 230 | CLAUDE.md | setuptestlint-formatstyle+5 | 86/100 | 9 days ago |
Same format, overlapping stack, ranked by quality.
| Repository | Format | Stack | Covers | Score | Changed |
|---|---|---|---|---|---|
| hiromaily/go-crypto-wallet.cursor/rules/typescript.mdc · 126 | Cursor rules | setupbuildtestlint-format+6 | 100/100 | 14 days ago | |
| TechSquidTV/Hermes.cursor/rules/10-hermes-api.mdc · 46 | Cursor rules | testlint-formatstylearch+5 | 100/100 | 14 days ago | |
| dodgecfr/combatfilms-webapp.cursor/rules/frontend.mdc · 0 | Cursor rules | setuptestlint-formatstyle+7 | 99/100 | 14 days ago | |
| deifos/clipmira-subtitles.cursor/rules/frontend.mdc · 1 | Cursor rules | setuptestlint-formatstyle+7 | 99/100 | 14 days ago | |
| markstev/mark-starter.cursor/rules/frontend.mdc · 0 | Cursor rules | setuptestlint-formatstyle+6 | 99/100 | 14 days ago | |
| Allymahmoud/case-intake-platform.cursor/rules/frontend.mdc · 0 | Cursor rules | setuptestlint-formatstyle+7 | 99/100 | 14 days ago | |
| langflow-ai/langflow.cursor/rules/docs_development.mdc · 153k | Cursor rules | setupbuildtestlint-format+7 | 97/100 | 14 days ago | |
| bybren-llc/safe-agentic-workflow.cursor/rules/10-backend-python.mdc · 399 | Cursor rules | testlint-formatstylegit+4 | 97/100 | today |
A badge carrying the measured quality of the strongest agent config file in this repository, out of 100. It reads from this index every time somebody loads your page, so it changes when the measurement changes and there is nothing to keep up to date. Free, no account, and the value is not something you or we can set by hand.
[](https://rulestack.kynth.studio/configs/tyrchen-geektime-bootcamp-ai-cursor-rules-rust-best-practices)Would rather not hotlink us? Every badge is also served in shields.io’s endpoint schema, so shields renders the image and your readers never talk to our domain:
Published by Toolproof, the masthead over this index and eight others. The method behind the number is at toolproof.kynth.studio/methodology, and the whole thing is readable as JSON with no key at /api.