rust-patterns
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseRust Development Patterns
Rust 开发模式
Idiomatic Rust patterns and best practices for building safe, performant, and maintainable applications.
构建安全、高性能且可维护应用的地道Rust编程模式与最佳实践。
When to Use
适用场景
- Writing new Rust code
- Reviewing Rust code
- Refactoring existing Rust code
- Designing crate structure and module layout
- 编写新的Rust代码
- 评审Rust代码
- 重构现有Rust代码
- 设计Crate结构与模块布局
How It Works
工作原理
This skill enforces idiomatic Rust conventions across six key areas: ownership and borrowing to prevent data races at compile time, / error propagation with for libraries and for applications, enums and exhaustive pattern matching to make illegal states unrepresentable, traits and generics for zero-cost abstraction, safe concurrency via , channels, and async/await, and minimal surfaces organized by domain.
Result?thiserroranyhowArc<Mutex<T>>pub本技能在六大核心领域强制执行地道的Rust规范:通过所有权与借用在编译期防止数据竞争;在库中使用/结合进行错误传播,在应用中使用;使用枚举与穷举模式匹配让非法状态无法被表示;使用特性与泛型实现零成本抽象;通过、通道和async/await实现安全并发;按领域组织最小化的对外接口。
Result?thiserroranyhowArc<Mutex<T>>pubCore Principles
核心原则
1. Ownership and Borrowing
1. 所有权与借用
Rust's ownership system prevents data races and memory bugs at compile time.
rust
// Good: Pass references when you don't need ownership
fn process(data: &[u8]) -> usize {
data.len()
}
// Good: Take ownership only when you need to store or consume
fn store(data: Vec<u8>) -> Record {
Record { payload: data }
}
// Bad: Cloning unnecessarily to avoid borrow checker
fn process_bad(data: &Vec<u8>) -> usize {
let cloned = data.clone(); // Wasteful — just borrow
cloned.len()
}Rust的所有权系统可在编译期防止数据竞争与内存漏洞。
rust
// 推荐:不需要所有权时传递引用
fn process(data: &[u8]) -> usize {
data.len()
}
// 推荐:仅在需要存储或消费时获取所有权
fn store(data: Vec<u8>) -> Record {
Record { payload: data }
}
// 不推荐:为规避借用检查器而不必要地克隆
fn process_bad(data: &Vec<u8>) -> usize {
let cloned = data.clone(); // 浪费资源 — 只需借用即可
cloned.len()
}Use Cow
for Flexible Ownership
Cow使用Cow
实现灵活所有权
Cowrust
use std::borrow::Cow;
fn normalize(input: &str) -> Cow<'_, str> {
if input.contains(' ') {
Cow::Owned(input.replace(' ', "_"))
} else {
Cow::Borrowed(input) // Zero-cost when no mutation needed
}
}rust
use std::borrow::Cow;
fn normalize(input: &str) -> Cow<'_, str> {
if input.contains(' ') {
Cow::Owned(input.replace(' ', "_"))
} else {
Cow::Borrowed(input) // 无需修改时零成本
}
}Error Handling
错误处理
Use Result
and ?
— Never unwrap()
in Production
Result?unwrap()使用Result
和?
— 生产环境中绝不使用unwrap()
Result?unwrap()rust
// Good: Propagate errors with context
use anyhow::{Context, Result};
fn load_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 = toml::from_str(&content)
.with_context(|| format!("failed to parse config from {path}"))?;
Ok(config)
}
// Bad: Panics on error
fn load_config_bad(path: &str) -> Config {
let content = std::fs::read_to_string(path).unwrap(); // Panics!
toml::from_str(&content).unwrap()
}rust
// 推荐:携带上下文传播错误
use anyhow::{Context, Result};
fn load_config(path: &str) -> Result<Config> {
let content = std::fs::read_to_string(path)
.with_context(|| format!("从 {path} 读取配置失败"))?;
let config: Config = toml::from_str(&content)
.with_context(|| format!("解析 {path} 中的配置失败"))?;
Ok(config)
}
// 不推荐:发生错误时直接恐慌
fn load_config_bad(path: &str) -> Config {
let content = std::fs::read_to_string(path).unwrap(); // 会恐慌!
toml::from_str(&content).unwrap()
}Library Errors with thiserror
, Application Errors with anyhow
thiserroranyhow库错误使用thiserror
,应用错误使用anyhow
thiserroranyhowrust
// Library code: structured, typed errors
use thiserror::Error;
#[derive(Debug, Error)]
pub enum StorageError {
#[error("record not found: {id}")]
NotFound { id: String },
#[error("connection failed")]
Connection(#[from] std::io::Error),
#[error("invalid data: {0}")]
InvalidData(String),
}
// Application code: flexible error handling
use anyhow::{bail, Result};
fn run() -> Result<()> {
let config = load_config("app.toml")?;
if config.workers == 0 {
bail!("worker count must be > 0");
}
Ok(())
}rust
// 库代码:结构化、类型化错误
use thiserror::Error;
#[derive(Debug, Error)]
pub enum StorageError {
#[error("记录未找到:{id}")]
NotFound { id: String },
#[error("连接失败")]
Connection(#[from] std::io::Error),
#[error("无效数据:{0}")]
InvalidData(String),
}
// 应用代码:灵活的错误处理
use anyhow::{bail, Result};
fn run() -> Result<()> {
let config = load_config("app.toml")?;
if config.workers == 0 {
bail!("工作进程数必须大于 0");
}
Ok(())
}Option
Combinators Over Nested Matching
Option使用Option
组合子替代嵌套匹配
Optionrust
// Good: Combinator chain
fn find_user_email(users: &[User], id: u64) -> Option<String> {
users.iter()
.find(|u| u.id == id)
.map(|u| u.email.clone())
}
// Bad: Deeply nested matching
fn find_user_email_bad(users: &[User], id: u64) -> Option<String> {
match users.iter().find(|u| u.id == id) {
Some(user) => match &user.email {
email => Some(email.clone()),
},
None => None,
}
}rust
// 推荐:组合子链式调用
fn find_user_email(users: &[User], id: u64) -> Option<String> {
users.iter()
.find(|u| u.id == id)
.map(|u| u.email.clone())
}
// 不推荐:深层嵌套匹配
fn find_user_email_bad(users: &[User], id: u64) -> Option<String> {
match users.iter().find(|u| u.id == id) {
Some(user) => match &user.email {
email => Some(email.clone()),
},
None => None,
}
}Enums and Pattern Matching
枚举与模式匹配
Model States as Enums
用枚举建模状态
rust
// Good: Impossible states are unrepresentable
enum ConnectionState {
Disconnected,
Connecting { attempt: u32 },
Connected { session_id: String },
Failed { reason: String, retries: u32 },
}
fn handle(state: &ConnectionState) {
match state {
ConnectionState::Disconnected => connect(),
ConnectionState::Connecting { attempt } if *attempt > 3 => abort(),
ConnectionState::Connecting { .. } => wait(),
ConnectionState::Connected { session_id } => use_session(session_id),
ConnectionState::Failed { retries, .. } if *retries < 5 => retry(),
ConnectionState::Failed { reason, .. } => log_failure(reason),
}
}rust
// 推荐:非法状态无法被表示
enum ConnectionState {
Disconnected,
Connecting { attempt: u32 },
Connected { session_id: String },
Failed { reason: String, retries: u32 },
}
fn handle(state: &ConnectionState) {
match state {
ConnectionState::Disconnected => connect(),
ConnectionState::Connecting { attempt } if *attempt > 3 => abort(),
ConnectionState::Connecting { .. } => wait(),
ConnectionState::Connected { session_id } => use_session(session_id),
ConnectionState::Failed { retries, .. } if *retries < 5 => retry(),
ConnectionState::Failed { reason, .. } => log_failure(reason),
}
}Exhaustive Matching — No Catch-All for Business Logic
穷举匹配 — 业务逻辑中不要使用通配符
rust
// Good: Handle every variant explicitly
match command {
Command::Start => start_service(),
Command::Stop => stop_service(),
Command::Restart => restart_service(),
// Adding a new variant forces handling here
}
// Bad: Wildcard hides new variants
match command {
Command::Start => start_service(),
_ => {} // Silently ignores Stop, Restart, and future variants
}rust
// 推荐:显式处理每个变体
match command {
Command::Start => start_service(),
Command::Stop => stop_service(),
Command::Restart => restart_service(),
// 添加新变体时必须在此处处理
}
// 不推荐:通配符会隐藏新变体
match command {
Command::Start => start_service(),
_ => {} // 静默忽略Stop、Restart及未来新增的变体
}Traits and Generics
特性与泛型
Accept Generics, Return Concrete Types
接受泛型,返回具体类型
rust
// Good: Generic input, concrete output
fn read_all(reader: &mut impl Read) -> std::io::Result<Vec<u8>> {
let mut buf = Vec::new();
reader.read_to_end(&mut buf)?;
Ok(buf)
}
// Good: Trait bounds for multiple constraints
fn process<T: Display + Send + 'static>(item: T) -> String {
format!("processed: {item}")
}rust
// 推荐:泛型输入,具体输出
fn read_all(reader: &mut impl Read) -> std::io::Result<Vec<u8>> {
let mut buf = Vec::new();
reader.read_to_end(&mut buf)?;
Ok(buf)
}
// 推荐:为多个约束添加 trait 边界
fn process<T: Display + Send + 'static>(item: T) -> String {
format!("已处理:{item}")
}Trait Objects for Dynamic Dispatch
特性对象用于动态分发
rust
// Use when you need heterogeneous collections or plugin systems
trait Handler: Send + Sync {
fn handle(&self, request: &Request) -> Response;
}
struct Router {
handlers: Vec<Box<dyn Handler>>,
}
// Use generics when you need performance (monomorphization)
fn fast_process<H: Handler>(handler: &H, request: &Request) -> Response {
handler.handle(request)
}rust
// 当需要异构集合或插件系统时使用
trait Handler: Send + Sync {
fn handle(&self, request: &Request) -> Response;
}
struct Router {
handlers: Vec<Box<dyn Handler>>,
}
// 需要高性能时使用泛型(单态化)
fn fast_process<H: Handler>(handler: &H, request: &Request) -> Response {
handler.handle(request)
}Newtype Pattern for Type Safety
Newtype 模式实现类型安全
rust
// Good: Distinct types prevent mixing up arguments
struct UserId(u64);
struct OrderId(u64);
fn get_order(user: UserId, order: OrderId) -> Result<Order> {
// Can't accidentally swap user and order IDs
todo!()
}
// Bad: Easy to swap arguments
fn get_order_bad(user_id: u64, order_id: u64) -> Result<Order> {
todo!()
}rust
// 推荐:不同类型防止参数混淆
struct UserId(u64);
struct OrderId(u64);
fn get_order(user: UserId, order: OrderId) -> Result<Order> {
// 不会意外交换用户ID和订单ID
todo!()
}
// 不推荐:容易交换参数
fn get_order_bad(user_id: u64, order_id: u64) -> Result<Order> {
todo!()
}Structs and Data Modeling
结构体与数据建模
Builder Pattern for Complex Construction
构建器模式用于复杂对象构造
rust
struct ServerConfig {
host: String,
port: u16,
max_connections: usize,
}
impl ServerConfig {
fn builder(host: impl Into<String>, port: u16) -> ServerConfigBuilder {
ServerConfigBuilder { host: host.into(), port, max_connections: 100 }
}
}
struct ServerConfigBuilder { host: String, port: u16, max_connections: usize }
impl ServerConfigBuilder {
fn max_connections(mut self, n: usize) -> Self { self.max_connections = n; self }
fn build(self) -> ServerConfig {
ServerConfig { host: self.host, port: self.port, max_connections: self.max_connections }
}
}
// Usage: ServerConfig::builder("localhost", 8080).max_connections(200).build()rust
struct ServerConfig {
host: String,
port: u16,
max_connections: usize,
}
impl ServerConfig {
fn builder(host: impl Into<String>, port: u16) -> ServerConfigBuilder {
ServerConfigBuilder { host: host.into(), port, max_connections: 100 }
}
}
struct ServerConfigBuilder { host: String, port: u16, max_connections: usize }
impl ServerConfigBuilder {
fn max_connections(mut self, n: usize) -> Self { self.max_connections = n; self }
fn build(self) -> ServerConfig {
ServerConfig { host: self.host, port: self.port, max_connections: self.max_connections }
}
}
// 使用方式:ServerConfig::builder("localhost", 8080).max_connections(200).build()Iterators and Closures
迭代器与闭包
Prefer Iterator Chains Over Manual Loops
优先使用迭代器链而非手动循环
rust
// Good: Declarative, lazy, composable
let active_emails: Vec<String> = users.iter()
.filter(|u| u.is_active)
.map(|u| u.email.clone())
.collect();
// Bad: Imperative accumulation
let mut active_emails = Vec::new();
for user in &users {
if user.is_active {
active_emails.push(user.email.clone());
}
}rust
// 推荐:声明式、惰性求值、可组合
let active_emails: Vec<String> = users.iter()
.filter(|u| u.is_active)
.map(|u| u.email.clone())
.collect();
// 不推荐:命令式累加
let mut active_emails = Vec::new();
for user in &users {
if user.is_active {
active_emails.push(user.email.clone());
}
}Use collect()
with Type Annotation
collect()结合类型注解使用collect()
collect()rust
// Collect into different types
let names: Vec<_> = items.iter().map(|i| &i.name).collect();
let lookup: HashMap<_, _> = items.iter().map(|i| (i.id, i)).collect();
let combined: String = parts.iter().copied().collect();
// Collect Results — short-circuits on first error
let parsed: Result<Vec<i32>, _> = strings.iter().map(|s| s.parse()).collect();rust
// 收集为不同类型
let names: Vec<_> = items.iter().map(|i| &i.name).collect();
let lookup: HashMap<_, _> = items.iter().map(|i| (i.id, i)).collect();
let combined: String = parts.iter().copied().collect();
// 收集Result — 第一个错误出现时立即终止
let parsed: Result<Vec<i32>, _> = strings.iter().map(|s| s.parse()).collect();Concurrency
并发编程
Arc<Mutex<T>>
for Shared Mutable State
Arc<Mutex<T>>使用Arc<Mutex<T>>
实现共享可变状态
Arc<Mutex<T>>rust
use std::sync::{Arc, Mutex};
let counter = Arc::new(Mutex::new(0));
let handles: Vec<_> = (0..10).map(|_| {
let counter = Arc::clone(&counter);
std::thread::spawn(move || {
let mut num = counter.lock().expect("mutex poisoned");
*num += 1;
})
}).collect();
for handle in handles {
handle.join().expect("worker thread panicked");
}rust
use std::sync::{Arc, Mutex};
let counter = Arc::new(Mutex::new(0));
let handles: Vec<_> = (0..10).map(|_| {
let counter = Arc::clone(&counter);
std::thread::spawn(move || {
let mut num = counter.lock().expect("互斥锁已中毒");
*num += 1;
})
}).collect();
for handle in handles {
handle.join().expect("工作线程恐慌");
}Channels for Message Passing
使用通道进行消息传递
rust
use std::sync::mpsc;
let (tx, rx) = mpsc::sync_channel(16); // Bounded channel with backpressure
for i in 0..5 {
let tx = tx.clone();
std::thread::spawn(move || {
tx.send(format!("message {i}")).expect("receiver disconnected");
});
}
drop(tx); // Close sender so rx iterator terminates
for msg in rx {
println!("{msg}");
}rust
use std::sync::mpsc;
let (tx, rx) = mpsc::sync_channel(16); // 带背压的有界通道
for i in 0..5 {
let tx = tx.clone();
std::thread::spawn(move || {
tx.send(format!("消息 {i}")).expect("接收端已断开连接");
});
}
drop(tx); // 关闭发送端,使rx迭代器终止
for msg in rx {
println!("{msg}");
}Async with Tokio
使用Tokio实现异步编程
rust
use tokio::time::Duration;
async fn fetch_with_timeout(url: &str) -> Result<String> {
let response = tokio::time::timeout(
Duration::from_secs(5),
reqwest::get(url),
)
.await
.context("request timed out")?
.context("request failed")?;
response.text().await.context("failed to read body")
}
// Spawn concurrent tasks
async fn fetch_all(urls: Vec<String>) -> Vec<Result<String>> {
let handles: Vec<_> = urls.into_iter()
.map(|url| tokio::spawn(async move {
fetch_with_timeout(&url).await
}))
.collect();
let mut results = Vec::with_capacity(handles.len());
for handle in handles {
results.push(handle.await.unwrap_or_else(|e| panic!("spawned task panicked: {e}")));
}
results
}rust
use tokio::time::Duration;
async fn fetch_with_timeout(url: &str) -> Result<String> {
let response = tokio::time::timeout(
Duration::from_secs(5),
reqwest::get(url),
)
.await
.context("请求超时")?
.context("请求失败")?;
response.text().await.context("读取响应体失败")
}
// 生成并发任务
async fn fetch_all(urls: Vec<String>) -> Vec<Result<String>> {
let handles: Vec<_> = urls.into_iter()
.map(|url| tokio::spawn(async move {
fetch_with_timeout(&url).await
}))
.collect();
let mut results = Vec::with_capacity(handles.len());
for handle in handles {
results.push(handle.await.unwrap_or_else(|e| panic!("生成的任务恐慌:{e}")));
}
results
}Unsafe Code
不安全代码
When Unsafe Is Acceptable
可接受的不安全代码场景
rust
// Acceptable: FFI boundary with documented invariants (Rust 2024+)
/// # Safety
/// `ptr` must be a valid, aligned pointer to an initialized `Widget`.
unsafe fn widget_from_raw<'a>(ptr: *const Widget) -> &'a Widget {
// SAFETY: caller guarantees ptr is valid and aligned
unsafe { &*ptr }
}
// Acceptable: Performance-critical path with proof of correctness
// SAFETY: index is always < len due to the loop bound
unsafe { slice.get_unchecked(index) }rust
// 可接受:FFI边界,带有文档化的不变量(Rust 2024+)
/// # 安全性
/// `ptr` 必须是指向已初始化 `Widget` 的有效、对齐指针。
unsafe fn widget_from_raw<'a>(ptr: *const Widget) -> &'a Widget {
// 安全性:调用者保证ptr有效且对齐
unsafe { &*ptr }
}
// 可接受:性能关键路径,且有正确性证明
// 安全性:由于循环边界,index始终小于len
unsafe { slice.get_unchecked(index) }When Unsafe Is NOT Acceptable
不可接受的不安全代码场景
rust
// Bad: Using unsafe to bypass borrow checker
// Bad: Using unsafe for convenience
// Bad: Using unsafe without a Safety comment
// Bad: Transmuting between unrelated typesrust
// 不推荐:使用unsafe规避借用检查器
// 不推荐:为了方便使用unsafe
// 不推荐:使用unsafe但无Safety注释
// 不推荐:在不相关类型之间进行转换Module System and Crate Structure
模块系统与Crate结构
Organize by Domain, Not by Type
按领域组织,而非按类型
text
my_app/
├── src/
│ ├── main.rs
│ ├── lib.rs
│ ├── auth/ # Domain module
│ │ ├── mod.rs
│ │ ├── token.rs
│ │ └── middleware.rs
│ ├── orders/ # Domain module
│ │ ├── mod.rs
│ │ ├── model.rs
│ │ └── service.rs
│ └── db/ # Infrastructure
│ ├── mod.rs
│ └── pool.rs
├── tests/ # Integration tests
├── benches/ # Benchmarks
└── Cargo.tomltext
my_app/
├── src/
│ ├── main.rs
│ ├── lib.rs
│ ├── auth/ # 领域模块
│ │ ├── mod.rs
│ │ ├── token.rs
│ │ └── middleware.rs
│ ├── orders/ # 领域模块
│ │ ├── mod.rs
│ │ ├── model.rs
│ │ └── service.rs
│ └── db/ # 基础设施
│ ├── mod.rs
│ └── pool.rs
├── tests/ # 集成测试
├── benches/ # 基准测试
└── Cargo.tomlVisibility — Expose Minimally
可见性 — 最小化对外暴露
rust
// Good: pub(crate) for internal sharing
pub(crate) fn validate_input(input: &str) -> bool {
!input.is_empty()
}
// Good: Re-export public API from lib.rs
pub mod auth;
pub use auth::AuthMiddleware;
// Bad: Making everything pub
pub fn internal_helper() {} // Should be pub(crate) or privaterust
// 推荐:使用pub(crate)进行内部共享
pub(crate) fn validate_input(input: &str) -> bool {
!input.is_empty()
}
// 推荐:在lib.rs中重新导出公共API
pub mod auth;
pub use auth::AuthMiddleware;
// 不推荐:将所有内容设为pub
pub fn internal_helper() {} // 应该设为pub(crate)或私有Tooling Integration
工具集成
Essential Commands
必备命令
bash
undefinedbash
undefinedBuild and check
构建与检查
cargo build
cargo check # Fast type checking without codegen
cargo clippy # Lints and suggestions
cargo fmt # Format code
cargo build
cargo check # 快速类型检查,无需生成代码
cargo clippy # 代码检查与建议
cargo fmt # 代码格式化
Testing
测试
cargo test
cargo test -- --nocapture # Show println output
cargo test --lib # Unit tests only
cargo test --test integration # Integration tests only
cargo test
cargo test -- --nocapture # 显示println输出
cargo test --lib # 仅运行单元测试
cargo test --test integration # 仅运行集成测试
Dependencies
依赖管理
cargo audit # Security audit
cargo tree # Dependency tree
cargo update # Update dependencies
cargo audit # 安全审计
cargo tree # 依赖树
cargo update # 更新依赖
Performance
性能测试
cargo bench # Run benchmarks
undefinedcargo bench # 运行基准测试
undefinedQuick Reference: Rust Idioms
速查:Rust惯用写法
| Idiom | Description |
|---|---|
| Borrow, don't clone | Pass |
| Make illegal states unrepresentable | Use enums to model valid states only |
| Propagate errors, never panic in library/production code |
| Parse, don't validate | Convert unstructured data to typed structs at the boundary |
| Newtype for type safety | Wrap primitives in newtypes to prevent argument swaps |
| Prefer iterators over loops | Declarative chains are clearer and often faster |
| Ensure callers handle return values |
| Avoid allocations when borrowing suffices |
| Exhaustive matching | No wildcard |
Minimal | Use |
| 惯用写法 | 描述 |
|---|---|
| 优先借用,避免克隆 | 无需所有权时传递 |
| 让非法状态无法被表示 | 使用枚举仅建模有效状态 |
用 | 传播错误,绝不在库或生产代码中恐慌 |
| 解析而非验证 | 在边界处将非结构化数据转换为类型化结构体 |
| 使用Newtype实现类型安全 | 将基本类型包装为新类型,防止参数混淆 |
| 优先使用迭代器而非循环 | 声明式链更清晰且通常更快 |
对Result使用 | 确保调用者处理返回值 |
使用 | 无需修改时避免分配 |
| 穷举匹配 | 业务关键枚举不使用通配符 |
最小化 | 对内部API使用 |
Anti-Patterns to Avoid
需避免的反模式
rust
// Bad: .unwrap() in production code
let value = map.get("key").unwrap();
// Bad: .clone() to satisfy borrow checker without understanding why
let data = expensive_data.clone();
process(&original, &data);
// Bad: Using String when &str suffices
fn greet(name: String) { /* should be &str */ }
// Bad: Box<dyn Error> in libraries (use thiserror instead)
fn parse(input: &str) -> Result<Data, Box<dyn std::error::Error>> { todo!() }
// Bad: Ignoring must_use warnings
let _ = validate(input); // Silently discarding a Result
// Bad: Blocking in async context
async fn bad_async() {
std::thread::sleep(Duration::from_secs(1)); // Blocks the executor!
// Use: tokio::time::sleep(Duration::from_secs(1)).await;
}Remember: If it compiles, it's probably correct — but only if you avoid , minimize , and let the type system work for you.
unwrap()unsaferust
// 不推荐:生产代码中使用.unwrap()
let value = map.get("key").unwrap();
// 不推荐:为满足借用检查器而盲目克隆,却不理解原因
let data = expensive_data.clone();
process(&original, &data);
// 不推荐:可用&str时使用String
fn greet(name: String) { /* 应改为&str */ }
// 不推荐:库中使用Box<dyn Error>(应使用thiserror)
fn parse(input: &str) -> Result<Data, Box<dyn std::error::Error>> { todo!() }
// 不推荐:忽略must_use警告
let _ = validate(input); // 静默丢弃Result
// 不推荐:在异步上下文阻塞
async fn bad_async() {
std::thread::sleep(Duration::from_secs(1)); // 会阻塞执行器!
// 应使用:tokio::time::sleep(Duration::from_secs(1)).await;
}请记住:如果代码能编译通过,那么它很可能是正确的 — 但前提是你避免使用、最小化代码,并让类型系统为你工作。
unwrap()unsafe