rust 在为第三方结构体实现第三方特征时,有什么方法可以避免 Package 器

ybzsozfc  于 2023-10-20  发布在  其他
关注(0)|答案(1)|浏览(109)

我正在实现一个Rust库,它需要一个异步mpsc通道,但是,我不想依赖于任何特定的async运行时(例如,tokiosmol),我想让库的用户选择使用哪一个,所以我定义了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 器了?
或者其他的变通办法?

im9ewurl

im9ewurl1#

不能,不能基于类型上可用的函数创建一揽子实现。类型实现trait的方式只有三种:

  1. trait的所有者为类型实现它。
  2. trait的所有者创建一个blanket implementation,其中包括可能绑定在 trait(s) a type implements上的类型。
    1.结构的所有者实现了该特征。
    就这样
    所以你可以
    1.在你的库中为所有应该实现trait的类型创建一个实现,可能每个运行时都有一个功能,这样用户就可以选择他们需要支持的运行时。
    1.向实现trait的运行时提交补丁(但通常它们的运行时不会关心你的库)
    1.创建一个覆盖所有类型的实现(尽管这对tokiosmol都不起作用,因为send是它们各自Sender上的固有函数)
    1.让用户创建 Package 器并为他们想要的类型实现trait。
    当然,你可以写一个派生宏:
use proc_macro::TokenStream;
use quote::quote;

#[proc_macro_derive(Sender)]
fn derive_sender(input: TokenStream) -> TokenStream {
    let input: syn::DeriveInput = syn::parse(input).unwrap();
    let t = input.ident;
    quote!{
        impl ::mycrate::Sender for #t {
            fn send(&mut self, job: ::mycrate::Job) {
                self.0.send(job);
            }
        }
    }
}

或其他宏,使4更容易。

相关问题