#traits #dyn #generic #macro #generics #impl-block

traitify

将impl块转换为特质的宏

1 个不稳定版本

0.1.0 2023年6月6日

#1041 in 进程宏

MIT/Apache

17KB
178

Traitify

crates.io Documentation

你应该使用这个crate吗?

不。可能不是。

为什么我要创建这个crate?

好吧,有时候你有一个泛型类型,需要将其传递给一个不能有泛型的函数。如果Rust有类似于以下语法的语法那就太棒了:

struct Foo<BAR, const FIZZ: usize>;

fn buzz(foo: &Foo<dyn, dyn>) {}

看我是怎么做的?在泛型类型的位置使用dyn的这种假想语法会动态地将它转换为特质对象!酷吧?

遗憾的是,这种语法并不是真实的,实际上并没有类似的东西。

那么,有哪些可行的方法呢?

基本上,我能想到两种方法。

如果你知道所有泛型的不同变体,你可以创建一个枚举,并在该枚举上重新实现所有的Foo函数。

如果你不知道,那么你可以创建一个包含Foo所有函数的特质,并为Foo实现该特质。但仅由一个类型实现的特质感觉是错误的。它感觉像是写了一个C头文件,我就完成写头文件的工作了。

这就是这个crate的作用所在!

这个crate只包含一个宏,你可以将其应用到impl块上。你告诉它特质的名称以及它应该忽略哪些泛型类型。

use traitify::traitify;

struct Foo<BAR, const FIZZ: usize> {
    data: [BAR; FIZZ],
};

#[traitify(FooDynFizz, dyn = [FIZZ])]
#[traitify(FooDynBAR, dyn = [BAR])]
#[traitify(FooDynAll, dyn = [FIZZ, BAR])]
impl<BAR, const FIZZ: usize> Foo<BAR, FIZZ> {
    pub fn print_greeting(&self) {
        println!("Hallo vriendjes en vriendinnetjes!");
    }
}

#[test]
fn compiles() {
    let foo = Foo::<u8, 4> { data: [0; 4] };
    let dyn_foo: &dyn FooDynAll = &foo;
    dyn_foo.print_greeting();
}

现在,我已尽力使这个宏处理尽可能多的语法。但如果发现某些应该工作但未工作的语法,请提交问题或PR!

以下是一个(可能不完整)的列表,列出了哪些函数最终出现在生成的特质中

  • 函数必须是公开的(这不是技术要求,但否则你将通过公开的特质泄露私有函数)
  • 函数不能有在dyn列表中的泛型类型的参数
  • 函数不能指定ABI
  • 函数必须有一个接收器参数(self, &self 或 &mut self)。它将在特质中,但为了对象安全,会加上一个 Self: Sized 绑定。对特质对象来说不是很实用,但它添加起来很简单。

不能擦除生命周期。我认为应该是可能的,但我还没有实现。

依赖项

~265–710KB
~17K SLoC