Skip to main content

rust-development

Rust language expertise for writing safe, performant, production-quality Rust code. Primary language for the Loom project. Use for Rust development, ownership patterns, error handling, async/await, cargo management, CLI tools, and serialization.

소스 정보

저장소
zekdevs/pi-config
최근 소스 활동
2026년 5월 17일 04:55
감지된 SKILL.md 언어
영어
스타
34
포크
3

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
rust-development
description
Rust language expertise for writing safe, performant, production-quality Rust code. Primary language for the Loom project. Use for Rust development, ownership patterns, error handling, async/await, cargo management, CLI tools, and serialization.
## Cargo and Project Structure ```toml # Cargo.toml [package] name = "myproject" version = "0.1.0" edition = "2021" rust-version = "1.75" [dependencies] tokio = { version = "1.35", features = ["full"] } serde = { version = "1.0", features = ["derive"] } thiserror = "1.0" anyhow = "1.0" [dev-dependencies] criterion = "0.5" mockall = "0.12" [features] default = [] full = ["feature-a", "feature-b"] feature-a = [] feature-b = ["dep:optional-dep"] [[bench]] name = "my_benchmark" harness = false ``` ## Workspace Structure ```text myworkspace/ ├── Cargo.toml # Workspace root ├── crates/ │ ├── core/ │ │ ├── Cargo.toml │ │ └── src/ │ ├── api/ │ │ ├── Cargo.toml │ │ └── src/ │ └── cli/ │ ├── Cargo.toml │ └── src/ ``` ## Key Concepts ### Ownership, Borrowing, and Lifetimes ```rust // Ownership rules: // 1. Each value has exactly one owner // 2. When the owner goes out of scope, the value is dropped // 3. Ownership can be transferred (moved) or borrowed // Move semantics fn take_ownership(s: String) { println!("{}", s); } // s is dropped here fn main() { let s = String::from("hello"); take_ownership(s); // s is no longer valid here } // Borrowing (references) fn borrow(s: &String) { println!("{}", s); } fn borrow_mut(s: &mut String) { s.push_str(" world"); } // Lifetimes ensure references are valid fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { if x.len() > y.len() { x } else { y } } // Struct with lifetime annotations struct Parser<'a> { input: &'a str, position: usize, } impl<'a> Parser<'a> { fn new(input: &'a str) -> Self { Parser { input, position: 0 } } fn peek(&self) -> Option<char> { self.input[self.position..].chars().next() } } // Common lifetime elision patterns impl Config { // fn get(&self, key: &str) -> Option<&str> // is short for: // fn get<'a, 'b>(&'a self, key: &'b str) -> Option<&'a str> fn get(&self, key: &str) -> Option<&str> { self.map.get(key).map(|s| s.as_str()) } } ``` ### Error Handling ```rust use std::error::Error; use std::fmt; use std::io; // Using thiserror for custom errors use thiserror::Error; #[derive(Error, Debug)] pub enum AppError { #[error("IO error: {0}")] Io(#[from] io::Error), #[error("Parse error at line {line}: {message}")] Parse { line: usize, message: String }, #[error("Not found: {0}")] NotFound(String), #[error("Validation failed: {0}")] Validation(String), } // Using anyhow for application code use anyhow::{Context, Result, bail, ensure}; fn read_config(path: &str) -> Result<Config> { let content = std::fs::read_to_string(path) .with_context(|| format!("Failed to read config from {}", path))?; let config: Config = serde_json::from_str(&content) .context("Failed to parse config JSON")?; ensure!(!config.name.is_empty(), "Config name cannot be empty"); if config.port == 0 { bail!("Invalid port number"); } Ok(config) } // The ? operator for propagating errors fn process_file(path: &str) -> Result<Vec<Record>, AppError> { let content = std::fs::read_to_string(path)?; // io::Error -> AppError via From let records = parse_records(&content)?; Ok(records) } // Option handling fn find_user(users: &[User], name: &str) -> Option<&User> { users.iter().find(|u| u.name == name) } fn get_user_email(users: &[User], name: &str) -> Option<String> { users .iter() .find(|u| u.name == name) .and_then(|u| u.email.clone()) } // Converting between Option and Result fn require_user(users: &[User], name: &str) -> Result<&User, AppError> { users .iter() .find(|u| u.name == name) .ok_or_else(|| AppError::NotFound(format!("User: {}", name))) } ``` ### Traits and Generics ```rust // Defining traits trait Repository<T> { fn get(&self, id: &str) -> Option<&T>; fn save(&mut self, item: T) -> Result<(), Box<dyn Error>>; // Default implementation fn exists(&self, id: &str) -> bool { self.get(id).is_some() } } // Trait bounds fn process<T: Clone + Debug>(item: &T) { let cloned = item.clone(); println!("{:?}", cloned); } // where clauses for complex bounds fn merge<T, U, V>(a: T, b: U) -> V where T: IntoIterator<Item = V>, U: IntoIterator<Item = V>, V: Ord + Clone, { let mut result: Vec<V> = a.into_iter().chain(b.into_iter()).collect(); result.sort(); result.dedup(); result.into_iter().next().unwrap() } // Associated types trait Iterator { type Item; fn next(&mut self) -> Option<Self::Item>; } // Implementing traits struct InMemoryRepo<T> { items: HashMap<String, T>, } impl<T: Clone> Repository<T> for InMemoryRepo<T> { fn get(&self, id: &str) -> Option<&T> { self.items.get(id) } fn save(&mut self, item: T) -> Result<(), Box<dyn Error>> { // Implementation Ok(()) } } // Blanket implementations impl<T: Display> ToString for T { fn to_string(&self) -> String { format!("{}", self) } } ``` ### Iterators ```rust // Iterator combinators fn process_users(users: Vec<User>) -> Vec<String> { users .into_iter() .filter(|u| u.active) .map(|u| u.email) .filter_map(|email| email) // Remove None values .collect() } // Custom iterator struct Counter { current: usize, max: usize, } impl Iterator for Counter { type Item = usize; fn next(&mut self) -> Option<Self::Item> { if self.current < self.max { let val = self.current; self.current += 1; Some(val) } else { None } } } // Useful iterator methods fn examples(numbers: Vec<i32>) { // Fold/reduce let sum: i32 = numbers.iter().fold(0, |acc, x| acc + x); // Any/all let has_positive = numbers.iter().any(|&x| x > 0); let all_positive = numbers.iter().all(|&x| x > 0); // Find let first_even = numbers.iter().find(|&&x| x % 2 == 0); // Partition let (evens, odds): (Vec<_>, Vec<_>) = numbers.iter().partition(|&&x| x % 2 == 0); // Enumerate for (index, value) in numbers.iter().enumerate() { println!("{}: {}", index, value); } // Zip let other = vec![1, 2, 3]; let pairs: Vec<_> = numbers.iter().zip(other.iter()).collect(); } ``` ## Anti-Patterns ### Avoid These Practices ```rust // BAD: Unnecessary clone fn process(items: &Vec<String>) { for item in items.clone() { // Unnecessary allocation println!("{}", item); } } // GOOD: Iterate by reference fn process(items: &[String]) { for item in items { println!("{}", item); } } // BAD: Using unwrap/expect in library code fn parse_config(s: &str) -> Config { serde_json::from_str(s).unwrap() // Panics on invalid input } // GOOD: Return Result and let caller handle errors fn parse_config(s: &str) -> Result<Config, serde_json::Error> { serde_json::from_str(s) } // BAD: Excessive use of Rc<RefCell<T>> struct Node { value: i32, children: Vec<Rc<RefCell<Node>>>, } // GOOD: Consider arena allocation or indices struct Arena { nodes: Vec<Node>, } struct Node { value: i32, children: Vec<usize>, // Indices into arena } // BAD: String concatenation in loops fn build_message(parts: &[&str]) -> String { let mut result = String::new(); for part in parts { result = result + part + ", "; // Creates new String each iteration } result } // GOOD: Use push_str or collect fn build_message(parts: &[&str]) -> String { parts.join(", ") } // BAD: Boxing errors unnecessarily fn parse(s: &str) -> Result<Data, Box<dyn Error>> { // For libraries, use concrete error types } // GOOD: Use concrete error types in libraries fn parse(s: &str) -> Result<Data, ParseError> { // thiserror for library errors, anyhow for applications } // BAD: Unsafe without justification unsafe fn get_unchecked(slice: &[i32], index: usize) -> i32 { *slice.get_unchecked(index) } // GOOD: Safe by default, unsafe with clear invariants fn get_unchecked(slice: &[i32], index: usize) -> i32 { // SAFETY: Caller must ensure index < slice.len() // Only use when bounds checking is a proven bottleneck debug_assert!(index < slice.len()); unsafe { *slice.get_unchecked(index) } } // BAD: Ignoring must_use let _ = fs::remove_file("temp.txt"); // Error silently ignored // GOOD: Handle the result fs::remove_file("temp.txt").ok(); // Explicitly ignore // or fs::remove_file("temp.txt")?; // Propagate error ```
GitHub에서 보기