name: rust-systems description: Rust 系统编程模式,包括所有权、特性、异步运行时、错误处理和不安全指南
Rust系统
所有权与借用
fn process_data(data: &[u8]) -> Vec<u8> {
data.iter().map(|b| b.wrapping_add(1)).collect()
}
fn modify_in_place(data: &mut Vec<u8>) {
data.retain(|b| *b != 0);
data.sort_unstable();
}
fn take_ownership(data: Vec<u8>) -> Vec<u8> {
let mut result = data;
result.push(0xFF);
result
}
fn main() {
let data = vec![1, 2, 3, 0, 4];
let processed = process_data(&data); // 借用:数据仍可用
let mut owned = take_ownership(data); // 移动:数据不再可用
modify_in_place(&mut owned); // 可变借用
}
优先使用借用(&T、&mut T)而非所有权转移。仅在必要时使用 Clone。
错误处理
use thiserror::Error;
#[derive(Error, Debug)]
pub enum AppError {
#[error("数据库错误: {0}")]
Database(#[from] sqlx::Error),
#[error("未找到: {resource} 的 id 为 {id}")]
NotFound { resource: &'static str, id: String },
#[error("验证失败: {0}")]
Validation(String),
}
type Result<T> = std::result::Result<T, AppError>;
async fn get_user(pool: &PgPool, id: &str) -> Result<User> {
sqlx::query_as::<_, User>("SELECT * FROM users WHERE id = $1")
.bind(id)
.fetch_optional(pool)
.await?
.ok_or_else(|| AppError::NotFound {
resource: "用户",
id: id.to_string(),
})
}
库错误使用 thiserror,应用级错误使用 anyhow。生产代码中避免使用 .unwrap()。
特性与泛型
trait Repository {
type Item;
type Error;
async fn find_by_id(&self, id: &str) -> std::result::Result<Option<Self::Item>, Self::Error>;
async fn save(&self, item: &Self::Item) -> std::result::Result<(), Self::Error>;
}
struct PgUserRepo {
pool: PgPool,
}
impl Repository for PgUserRepo {
type Item = User;
type Error = AppError;
async fn find_by_id(&self, id: &str) -> Result<Option<User>> {
let user = sqlx::query_as::<_, User>("SELECT * FROM users WHERE id = $1")
.bind(id)
.fetch_optional(&self.pool)
.await?;
Ok(user)
}
async fn save(&self, user: &User) -> Result<()> {
sqlx::query("INSERT INTO users (id, name, email) VALUES ($1, $2, $3)")
.bind(&user.id)
.bind(&user.name)
.bind(&user.email)
.execute(&self.pool)
.await?;
Ok(())
}
}
异步模式
use tokio::sync::Semaphore;
use futures::stream::{self, StreamExt};
async fn fetch_all(urls: Vec<String>, max_concurrent: usize) -> Vec<Result<String>> {
let semaphore = Arc::new(Semaphore::new(max_concurrent));
stream::iter(urls)
.map(|url| {
let sem = semaphore.clone();
async move {
let _permit = sem.acquire().await.unwrap();
reqwest::get(&url).await?.text().await.map_err(Into::into)
}
})
.buffer_unordered(max_concurrent)
.collect()
.await
}
async fn graceful_shutdown(handle: tokio::runtime::Handle) {
let ctrl_c = tokio::signal::ctrl_c();
ctrl_c.await.expect("监听 Ctrl+C 失败");
handle.shutdown_timeout(std::time::Duration::from_secs(30));
}
构建器模式
pub struct ServerConfig {
host: String,
port: u16,
workers: usize,
tls: bool,
}
pub struct ServerConfigBuilder {
host: String,
port: u16,
workers: usize,
tls: bool,
}
impl ServerConfigBuilder {
pub fn new() -> Self {
Self { host: "0.0.0.0".into(), port: 8080, workers: 4, tls: false }
}
pub fn host(mut self, host: impl Into<String>) -> Self { self.host = host.into(); self }
pub fn port(mut self, port: u16) -> Self { self.port = port; self }
pub fn workers(mut self, n: usize) -> Self { self.workers = n; self }
pub fn tls(mut self, enabled: bool) -> Self { self.tls = enabled; self }
pub fn build(self) -> ServerConfig {
ServerConfig { host: self.host, port: self.port, workers: self.workers, tls: self.tls }
}
}
反模式
- 在库代码中使用
.unwrap()或.expect() - 不必要地克隆数据而非借用
- 跨
.await点持有MutexGuard(导致死锁) - 使用
Arc<Mutex<Vec<T>>>而通道更合适 - 编写
unsafe代码时未记录不变量 - 在返回 Result 的函数上未使用
#[must_use]
清单
- [ ] 错误类型使用
thiserror定义,传播使用?运算符 - [ ] 生产路径中无
.unwrap() - [ ] 所有权模型最小化克隆
- [ ] 异步代码使用有界并发(信号量或
buffer_unordered) - [ ] 使用特性进行抽象和可测试性
- [ ]
unsafe块有记录的安全性不变量 - [ ] 复杂配置结构使用构建器模式
- [ ] 启用 Clippy 检查并处理警告