我正在实现一个Rust库,它需要一个异步mpsc通道,但是,我不想依赖于任何特定的async
运行时(例如,tokio
,smol
),我想让库的用户选择使用哪一个,所以我定义了traits:
struct Job { ... }
trait Sender {
fn send(&mut self, job: Job);
}
trait Receiver {
fn recv(&mut self) -> Future<Output = Option<Job>>;
}
fn foo(tx: impl Sender, rx: impl Receiver) {
// ...
}
然而,库用户必须在实际使用的运行时上创建大量的 Package 器,例如。对于时雄:
// We have to create wrappers here, as both `mylib::Sender`
// and `tokio::Sender` are from third party libraries, we
// cannot implement `mylib::Sender` for `tokio::Sender` directly.
struct TokioSender(tokio::Sender);
struct TokioReceiver(tokio::Receiver);
impl mylib::Sender for TokioSender { ... }
impl mylib::Receiver for TokioReceiver { ... }
// Finally we can call the library function.
fn run() {
...
let (tx, rx) = tokio::channel();
mylib::foo(TokioSender(tx), TokioReceiver(rx));
...
}
如果traits有很多功能, Package 器可能会很乏味。在Go语言中,如果类型T
具有接口I
定义的所有函数,那么T
实现了I
,并且可以在所需类型为I
的地方传递,而无需任何额外的努力。那么,有没有可能在这里做同样的事情,让所有具有trait定义的函数的T
像Go一样自动实现trait,这样我们就不再需要那些 Package 器了?
或者其他的变通办法?
1条答案
按热度按时间im9ewurl1#
不能,不能基于类型上可用的函数创建一揽子实现。类型实现trait的方式只有三种:
1.结构的所有者实现了该特征。
就这样
所以你可以
1.在你的库中为所有应该实现trait的类型创建一个实现,可能每个运行时都有一个功能,这样用户就可以选择他们需要支持的运行时。
1.向实现trait的运行时提交补丁(但通常它们的运行时不会关心你的库)
1.创建一个覆盖所有类型的实现(尽管这对
tokio
和smol
都不起作用,因为send
是它们各自Sender
上的固有函数)1.让用户创建 Package 器并为他们想要的类型实现trait。
当然,你可以写一个派生宏:
或其他宏,使4更容易。