You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Rust中实现结构体间传递回调且互不依赖?

在Rust中实现无依赖的结构体回调存储

完全可以实现,Rust提供了两种主流方式达成你的需求,既保证Bar不依赖Foo,又能存储并调用Foo的方法回调。

一、泛型版本(与C++模板实现对齐)

这种方式和你给出的C++代码逻辑几乎一致,通过泛型参数让Bar适配任意符合条件的回调类型,Bar的代码完全不需要知晓Foo的存在。

// 单独的foo模块,定义Foo结构体
mod foo {
    pub struct Foo;

    impl Foo {
        pub fn test(&self) {
            println!("Test");
        }
    }
}

// 单独的bar模块,完全不依赖foo模块
mod bar {
    pub struct Bar<Callback> {
        callback: Callback,
    }

    impl<Callback> Bar<Callback>
    where
        Callback: Fn(), // 约束回调是无参数无返回值的可调用对象
    {
        pub fn new(callback: Callback) -> Self {
            Bar { callback }
        }

        pub fn test(&self) {
            println!("Doing some stuff...");
            (self.callback)();
        }
    }
}

use foo::Foo;
use bar::Bar;

fn main() {
    let foo = Foo;
    // 传入捕获foo的闭包作为回调,Bar此时才和Foo建立关联
    let bar = Bar::new(|| {
        foo.test();
    });
    bar.test();
}

关键点说明

  • Bar是泛型结构体,泛型参数Callback仅要求满足Fn() trait(无参数、无返回值的可调用对象),不需要知道具体类型是捕获了Foo的闭包。
  • 依赖关系仅在main函数中建立,foo和bar两个模块彼此完全独立,符合你对依赖隔离的要求。
  • 这种方式是静态分发,没有运行时开销,和C++模板的编译期处理逻辑一致。

二、Trait对象版本(动态分发)

如果需要统一Bar的类型(比如在集合中存储不同回调的Bar实例),可以使用Box<dyn Fn()> trait对象实现动态分发,同样不需要Bar依赖Foo。

场景1:捕获栈上变量的回调

如果回调捕获的是栈上的Foo实例引用,需要通过生命周期参数绑定Bar的生命周期:

mod foo {
    pub struct Foo;

    impl Foo {
        pub fn test(&self) {
            println!("Test");
        }
    }
}

mod bar {
    pub struct Bar<'a> {
        // 绑定生命周期的trait对象,确保回调引用的变量存活时间不短于Bar
        callback: Box<dyn Fn() + 'a>,
    }

    impl<'a> Bar<'a> {
        pub fn new(callback: impl Fn() + 'a) -> Self {
            Bar {
                callback: Box::new(callback),
            }
        }

        pub fn test(&self) {
            println!("Doing some stuff...");
            (self.callback)();
        }
    }
}

use foo::Foo;
use bar::Bar;

fn main() {
    let foo = Foo;
    let bar = Bar::new(|| {
        foo.test();
    });
    bar.test();
}

场景2:捕获拥有所有权的变量或静态变量

如果回调捕获的是Foo的所有权,或者是静态变量,可以使用'static生命周期的trait对象,无需绑定Bar的生命周期:

mod foo {
    pub struct Foo;

    impl Foo {
        pub fn test(&self) {
            println!("Test");
        }
    }
}

mod bar {
    pub struct Bar {
        callback: Box<dyn Fn() + 'static>,
    }

    impl Bar {
        pub fn new(callback: impl Fn() + 'static) -> Self {
            Bar {
                callback: Box::new(callback),
            }
        }

        pub fn test(&self) {
            println!("Doing some stuff...");
            (self.callback)();
        }
    }
}

use foo::Foo;
use bar::Bar;

fn main() {
    let foo = Foo;
    // 使用move闭包获取foo的所有权
    let bar = Bar::new(move || {
        foo.test();
    });
    bar.test();
}

关键点说明

  • Trait对象方式允许Bar使用统一的类型,适合需要多态的场景,但会带来轻微的运行时开销(虚函数调用)。
  • Bar的代码依然完全不依赖Foo,仅通过trait约束回调的行为,满足依赖隔离的要求。

内容的提问来源于stack exchange,提问作者dovahin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 23:17:40