2个版本
0.1.1 | 2022年3月11日 |
---|---|
0.1.0 | 2022年3月11日 |
#486 in 过程宏
74KB
1.5K SLoC
提供在运行时实现具有多个动态参数调用的函数的宏。
![double_dyn!] 宏将定义指定的特质,并为所有提供类型生成实现,然后生成调用适当实现的函数。
用法
在您的 Cargo.toml 中
[dependencies]
double-dyn = "0.1.1"
基本用法
double_dyn!
宏调用包含3个部分。
- A和B特质的名称,以及任何子特质界限
- 函数原型
- 类型对的实现,形式为
<A, B>
示例
use double_dyn::double_dyn;
double_dyn!{
type A: MyTraitA;
type B: MyTraitB: std::fmt::Display;
fn multiply(a: &dyn MyTraitA, b: &dyn MyTraitB) -> Box<dyn MyTraitB>;
impl for <i32, String>
{
fn multiply(a: &i32, b: &String) -> Box<dyn MyTraitB> {
let multiplied_val = *a * b.parse::<i32>().unwrap();
Box::new(multiplied_val.to_string())
}
}
impl for <[i8, i16, i32, i64, i128], [f32, f64]>
{
fn multiply(a: &#A, b: &#B) -> Box<dyn MyTraitB> {
Box::new((*a as #B) * *b)
}
}
}
let val = multiply(&2, &7.5);
assert_eq!(format!("{}", val), "15");
上述宏调用将定义 MyTraitA
和 MyTraitB
特质,并为所有相关类型提供实现。
如上所示,可以在方括号中指定多个 A
和/或 B
类型。
您可以在实现块中显式使用具体类型,或者使用 #A
和 #B
标记作为函数签名和实现体内的别名,它们将在编译时被它们所代表的类型替换。
# use double_dyn::double_dyn;
double_dyn!{
type A: MyTrait: std::fmt::Display;
type B: MyTrait;
fn multiply(a: &dyn MyTrait, b: &dyn MyTrait) -> Box<dyn MyTrait>;
#[commutative]
impl for <[i8, i16, i32, i64, i128], [f32, f64]>
{
fn multiply(a: &#A, b: &#B) -> Box<dyn MyTrait> {
Box::new((*a as #B) * *b)
}
}
}
let val = multiply(&7.0, &2);
assert_eq!(format!("{}", val), "14");
可以为A和B提供相同的特质。在实现中,A和B的参数可能仍然是不同的类型。宏将尝试从使用 #A
或 #B
标记来推断哪个参数是 A
以及哪个是 B
,但如果存在歧义,则假定第一个 &dyn MyTrait
参数是 A
。
使用 #[commutative]
属性会在 A
被替换为 B
以及反之时生成额外的实现。
如果 A
和 B
特性相同,则 A
特性的界限将优先。
你可以在同一个 double_dyn
宏调用中声明多个函数,并且所有函数将使用相同的特性。然而,每个声明的函数都必须在每个 impl
块中实现。
更多的使用示例可以在这里的测试中找到。
限制
-
所有的
impls
必须在同一个double_dyn
宏调用中,包括定义。我希望能够支持将声明与实现分开,并允许根据需要添加额外的impls
,但我没有一种稳健的方法来在每次宏调用之间进行通信。这被 这个问题 所阻塞。 -
每个
double_dyn
宏调用定义一个特性或一对特性。此宏不是用来向现有特性添加方法的。你可以使用这个宏来定义一个特性,然后让这个特性成为你定义的另一个特性的超特性,从而允许在特性上使用双-dyn 方法。但是,由于稳定编译器中缺少 特性向上转换,这仍然是一个限制。如果你有关于如何让添加方法到现有特性更好的想法,请与我联系。 -
函数不能有泛型参数。这是基于函数被转换成特性方法的根本限制,而特性需要保持对象安全。
-
impl
不支持泛型“通用实现”。A
类型永远不能支持泛型类型,原因如上;对象安全性禁止在特性方法中使用泛型。B
类型理论上可以支持通用实现,但目前该宏不能解析impl
中的where
子句。如果你认为这个功能很重要,请告诉我,我可以添加它。 -
例如,可见性限定符(如
pub
),必须对每个函数原型相同。可见性将应用于所有生成的特性和函数。 -
不支持传递自有参数。例如,一个参数必须是
&dyn ATrait
的形式,而不是Box<dyn ATrait>
。 -
可能会报告多次错误和警告。
未来愿景
我希望能够通过 impl
块添加新的函数实现,这些块不是原始调用的一部分。换句话说,允许函数签名部分在代码中,并允许在其他地方添加额外的实现。不幸的是,我不认为这是可能的,因为 Rust 没有能力在宏调用之间进行通信。这在此处进行了讨论。
我还希望为在现有特性上实现方法提供更多灵活性。请参阅上面的 限制 部分。我对您认为有用的建议持开放态度。
致谢
依赖关系
~120KB