装饰器模式
问题
Rust 中如何实现装饰器模式?
答案
Rust 没有类继承,装饰器通过函数组合或 trait + 包装结构体实现。
方式 1:函数装饰器
use std::time::Instant;
// 原始函数
fn compute(x: i32) -> i32 {
std::thread::sleep(std::time::Duration::from_millis(100));
x * x
}
// 装饰器:添加计时功能
fn with_timing<F, R>(name: &str, f: F) -> R
where
F: FnOnce() -> R,
{
let start = Instant::now();
let result = f();
println!("{}: {:?}", name, start.elapsed());
result
}
// 使用
let result = with_timing("compute", || compute(42));
方式 2:HTTP 中间件(最常见的装饰器应用)
Tower 的 Layer 本质就是装饰器模式:
use tower::{Service, Layer, ServiceBuilder};
// 自定义 Layer:给响应添加请求 ID
#[derive(Clone)]
struct RequestIdLayer;
impl<S> Layer<S> for RequestIdLayer {
type Service = RequestIdService<S>;
fn layer(&self, inner: S) -> Self::Service {
RequestIdService { inner }
}
}
#[derive(Clone)]
struct RequestIdService<S> {
inner: S, // 被装饰的服务
}
// RequestIdService 包装了 inner 服务,在调用前后添加逻辑
// 这就是装饰器模式的本质
方式 3:trait 装饰器
trait Logger {
fn log(&self, msg: &str);
}
struct ConsoleLogger;
impl Logger for ConsoleLogger {
fn log(&self, msg: &str) {
println!("[LOG] {}", msg);
}
}
// 装饰器:添加时间戳
struct TimestampLogger<L: Logger> {
inner: L,
}
impl<L: Logger> Logger for TimestampLogger<L> {
fn log(&self, msg: &str) {
let now = chrono::Utc::now().format("%H:%M:%S");
self.inner.log(&format!("[{}] {}", now, msg));
}
}
// 装饰器可以嵌套
let logger = TimestampLogger {
inner: ConsoleLogger,
};
logger.log("hello"); // [LOG] [14:30:00] hello
常见面试问题
Q1: Rust 的装饰器和 TypeScript/Python 装饰器的区别?
答案:
| 维度 | Rust | TypeScript/Python |
|---|---|---|
| 实现方式 | 函数组合 / trait 包装 | 语言内置 @decorator 语法 |
| 类型安全 | 编译时检查 | 运行时 |
| 性能 | 可内联,零成本 | 有运行时开销 |
| 灵活性 | 需手动组合 | 语法糖更方便 |
在 Rust Web 生态中,Tower Layer 是装饰器模式最成功的应用。