17370845950

Rust 中实现 Java Consumer 接口风格的日志策略模式

本文介绍如何在 rust 中模拟 java 的 `consumer` 接口,通过泛型 + 闭包 trait(`fn`, `fnmut`)构建统一的 `logger` 策略抽象,支持无状态 lambda、有状态结构体和普通函数,实现灵活可插拔的日志行为。

在 Rust 中,虽然没有直接对应 Java Consumer 的内置接口,但标准库提供了三类核心闭包 trait:Fn(&T), FnMut(&mut T) 和 FnOnce(T),它们天然承担了“接受输入、执行副作用、不返回值

”的语义——这正是 Consumer 的本质。关键在于:Rust 的策略抽象应优先面向行为(closure),而非继承(interface)

下面是一个生产就绪的 Logger 实现,兼顾简洁性、类型安全与策略统一性:

#[derive(Debug, Clone, Copy)]
enum Event {
    BuyEvent,
    SellEvent,
}

// 泛型 Logger:封装任意可调用对象,要求其能消费 Event
struct Logger
where
    F: Fn(Event),
{
    consumer: F,
}

impl Logger
where
    F: Fn(Event),
{
    fn new(consumer: F) -> Self {
        Self { consumer }
    }

    fn accept(&self, event: Event) {
        (self.consumer)(event);
    }
}

// 示例:有状态日志器(如写入文件、计数等)
struct StatefulLogger {
    count: usize,
}

impl StatefulLogger {
    fn new() -> Self {
        Self { count: 0 }
    }

    fn log(&mut self, event: Event) {
        self.count += 1;
        println!("[{}] {:?}", self.count, event);
    }
}

fn main() {
    // ✅ 1. 无状态闭包(类似 Java lambda)
    let logger_lambda = Logger::new(|e| println!("Lambda: {:?}", e));

    // ✅ 2. 普通函数(类似 Java 方法引用)
    fn simple_log(e: Event) {
        println!("Fn: {:?}", e);
    }
    let logger_fn = Logger::new(simple_log);

    // ✅ 3. 有状态结构体 → 通过 move 闭包捕获并调用
    let mut stateful = StatefulLogger::new();
    let logger_stateful = Logger::new(move |e| stateful.log(e));

    // 统一调用入口
    logger_lambda.accept(Event::BuyEvent);
    logger_fn.accept(Event::SellEvent);
    logger_stateful.accept(Event::BuyEvent); // 注意:此处 stateful 已被 move 进闭包,无法复用
}

⚠️ 重要注意事项

  • 若需多次使用有状态对象(如 StatefulLogger),应改用 FnMut + &mut self 方式:将 Logger 改为 struct Logger where F: FnMut(&mut StatefulLogger, Event),或更推荐——直接让 StatefulLogger 自身实现 FnMut(即 impl FnMut for StatefulLogger),但这需手动实现 call_mut,通常不必要;
  • Fn 要求闭包只读捕获环境;若需修改捕获变量(如计数器),请改用 FnMut 并调整 accept 为 &mut self;
  • 避免过度泛型:若所有日志器都需共享生命周期或动态分发,可结合 Box 或 Arc>,但会牺牲零成本抽象优势。

总结:Rust 的策略模式不依赖接口继承,而依托于 trait object 或泛型闭包。Logger 是零成本抽象的理想载体——编译期单态化保证性能,类型系统确保安全,一行 Logger::new(...) 即可桥接函数、闭包与结构体方法,完美复刻 Java Consumer 的灵活性与表现力。