我正在构建一个应用程序。它有一个核心构件,我称之为“大脑”,它了解所有子系统(例如 DB)。
我不想直接使用任何特定的 DB 实现,而是使用特征对象(或任何可以实现相同目的的东西)。
所以看起来就像
struct Brain {
db: Box<dyn DbTrait>,
}
但是,我也希望所有代码都是高效的,因此我使用 tokio,并且大多数内容都是异步的
trait DbTrait {
fn do_something(&self) -> impl Future<Output = Whatever>;
}
这会产生冲突,因为 Rust 无法从返回impl
类型的特征中创建特征对象,如下面的 MWE 所强调的那样:
trait ReturnsFutures {
fn futuristic(&self) -> impl Future<Output = ()>;
}
struct FutureReturner {}
impl ReturnsFutures for FutureReturner {
async fn futuristic(&self) {
println!("just fake async");
}
}
struct OtherFutureReturner {}
impl ReturnsFutures for OtherFutureReturner {
async fn futuristic(&self) {
println!("also fakin' it");
}
}
struct FuturisticService {
future_returner: Box<dyn ReturnsFutures>,
}
给出错误
the trait `ReturnsFutures` cannot be made into an object
consider moving `futuristic` to another trait
the following types implement the trait, consider defining an enum where each variant holds one of these types, implementing `ReturnsFutures` for this new enum and using it instead:
FutureReturner
OtherFutureReturnerrustcClick for full compiler diagnostic
目前,我能想到一种解决方法,即改为异步,特征可以返回一个通道来等待结果。虽然我觉得更复杂,但这可能是相当可行的。对于这类问题,还有其他更可行/惯用的解决方案吗?
对此,旧的和经过测试的 crate 是
async_trait
。它甚至在 Traits 中的 async fn(和 Traits 中的 impl trait)稳定之前就存在了,并且当时是异步 Traits 的主要机制。它仍然运行良好,但这意味着您必须付出代价来装箱返回的 Future 并动态调度它,即使您不需要dyn DbTrait
。dynosaur
是 Rust 语言团队开发的一个较新的 crate,旨在让您使用 async fns/impl 特征动态分派特征。有了它,您可以继续像往常一样在泛型中使用特征,而无需任何开销,但它还会生成一个DynDbTrait
结构体,充当Box<dyn DbTrait>