我正在实现一个Rust库,它需要一个异步MPSC通道,但是,我不想依赖于任何特定的async运行时(例如tokiosmol),我想让库的用户 Select 使用哪个运行时,所以我定义了一些特征:

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) {
    // ...
}

然而,库用户必须在实际使用的运行时创建大量包装器,例如为Tokio:

// 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));
    ...
}

如果特征有很多功能,包装器可能会很繁琐.在围棋中,如果类型T具有接口I定义的所有函数,则T实现I,并且可以在所需类型为I的地方传递,而不需要任何额外的努力.那么,有没有可能在这里做同样的事情,让所有具有由特征定义的函数的T自动实现像GO这样的特征,这样我们就不再需要那些包装器了?

或者可能有任何其他解决办法?

推荐答案

不能,您不能基于类型上可用的函数来创建一揽子实现.类型实现特征的方式只有三种:

  1. 特征的所有者为该类型实现它.
  2. 特征的所有者创建一个一揽子实现,其中包括可能绑定在trait(s)个a类型实现上的类型.
  3. struct 的所有者实现了特征.

就这样.

这样你就可以

  1. 为库中应该实现特征的所有类型创建一个实现,每个运行时可能有gated by a feature个,这样用户就可以 Select 他们需要支持哪些运行时.
  2. 将补丁提交到实现特征的运行时(但通常它们的运行时不关心您的库)
  3. 创建一个涵盖所有您想要的类型的一揽子实现(尽管这不适用于tokiosmol,因为send是它们各自的Sender的固有函数)
  4. 让用户为他们想要的类型创建包装器并实现特征.

当然,您可以编写派生宏:

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更容易.

Rust相关问答推荐

如何使用Match比较 struct 中的值

我如何在Rust中使用传递依赖中的特征?

"value is never read警告似乎不正确.我应该忽略它吗?

如何实现泛型枚举的`Serde::Desialize`特性

在使用AWS SDK for Rust时,如何使用硬编码访问密钥ID和密钥凭据?

如何定义实现同名但返回类型不同的 struct 的函数

如何在Rust中将选项<;选项<;字符串>;转换为选项<;选项&;str>;?

为什么`AlternateScreen`在读取输入键时需要按Enter键?

在Rust 中移动原始指针的靶子安全吗

无法将 rust 蚀向量附加到另一个向量

&'a T 是否意味着 T: 'a?

如何将 struct 数组放置在另一个 struct 的末尾而不进行内存分段

结果流到 Vec 的结果:如何避免多个into_iter和collect?

如何基于常量在Rust中跳过一个测试

Rust中的标记特征是什么?

为什么 Rust 允许写入不可变的 RwLock?

Rust 中函数的类型同义词

只有一个字符被读入作为词法分析器的输入

没有通用参数的通用返回

以下打印数组每个元素的 Rust 代码有什么问题?