14 个版本
0.4.1 | 2024年7月18日 |
---|---|
0.3.7 | 2024年6月19日 |
0.3.6 | 2024年1月23日 |
0.3.5 | 2022年10月22日 |
0.1.1 | 2019年11月17日 |
#64 在 过程宏 中
15,576 每月下载量
用于 30 个crate(直接使用18个)
83KB
1K SLoC
Ambassador - 通过过程宏委托特质实现
通常,将特质的实现委托给枚举变体或结构体的字段需要大量的样板代码。Ambassador通过从过程宏推导委托特质实现来尝试消除这些样板代码。
最低支持的Rust版本是1.53.0。
安装
cargo add ambassador
通用用法
更多示例也可用 https://github.com/hobofan/ambassador/tree/master/ambassador/tests/run-pass
#[可委托特质]
首先,我们需要通过向它添加一个 #[delegatable_trait]
属性来使我们的特质可用于委托(这也使您的特质在其他crate中可委托)
use ambassador::delegatable_trait;
#[delegatable_trait] // <-------
pub trait Shout {
fn shout(&self, input: &str) -> String;
}
#[derive(Delegate)]
& #[delegate(Trait)]
现在我们可以通过添加 #[derive(Delegate)]
和其关联属性 #[delegate(Trait)]
来将我们的特质的实现委托给结构体字段/枚举变体
use ambassador::Delegate;
pub struct Cat;
impl Shout for Cat {
fn shout(&self, input: &str) -> String {
format!("{} - meow!", input)
}
}
#[derive(Delegate)] // <-------
#[delegate(Shout)] // <-------- Delegate implementation of Shout to struct field
pub struct WrappedCat(Cat);
#[delegate(..., target = "foo")]
- target
键
对于具有多个字段的结构体,可以通过 target
键指定应作为委托目标的字段
#[derive(Delegate)]
#[delegate(Shout, target = "foo")] // <-------- Delegate implementation of Shout to struct field .foo
pub struct WrappedCats {
foo: Cat,
bar: Cat,
}
这同样适用于具有多个字段的元组结构体,通过使用它们的索引作为目标键
#[derive(Delegate)]
#[delegate(Shout, target = "1")] // <-------- Delegate implementation of Shout to second field of type Dog
pub struct WrappedAnimals(Cat, Dog);
#[delegate(..., target = "self")]
- target="self"
实现了一个特质的所有方法而没有实现该特质本身的类型,可以通过设置 target="self"
来实现该特质。这对于具有关联类型和常量的特质不适用,并且需要显式添加 where 子句(参见 where
键)。如果类型实际上没有实现方法(可能是由于不完整的 where
子句),这可能会导致 [unconditional_recursion]
错误。
这种用法的一个例子是将公共类型的一些方法重构到特质中,由于 semver 的原因,类型仍需要在外部实现这些方法,使用此功能可以减少实现具有相同方法的特质时的冗余。
#[derive(Delegate)]
#[delegate(Shout, target="self")]
pub struct Cat;
impl Cat {
fn shout(&self, input: &str) -> String {
format!("{} - meow!", input)
}
}
#[delegate(..., where = "A: Shout")]
- where
键
为了使委托仅适用于某些泛型约束,类似于 本地 where 子句,可以指定一个 where
属性
将自动应用一个 where 子句,确保目标字段实现了正在委托的特质
#[derive(Delegate)]
#[delegate(Shout, where = "A: Debug")] // <---- Delegate implementation of Shout to .foo field if foo field implements Debug
pub struct WrappedFoo<A> {
foo: A,
}
#[delegate(Shout<X>)]
- 特质泛型
我们还可以使用泛型委托特质。在这种情况下,所有 X
和 'x
后跟任意数字的实例,例如 X0
X12
'x3
都被视为最大泛型。自动添加的 where 子句确保它们对所委托的内类型有效。可以像平常一样添加显式的 where 子句来进一步精炼这些类型。可以用特定的类型代替 X
来只为这些类型派生。
use ambassador::{delegatable_trait, Delegate};
use std::fmt::Display;
#[delegatable_trait] // <-------
pub trait Shout<T> {
fn shout(&self, input: T) -> String;
}
pub struct Cat;
impl<T: Display> Shout<T> for Cat {
fn shout(&self, input: T) -> String {
format!("{} - meow!", input)
}
}
#[derive(Delegate)]
#[delegate(Shout<X>, generics = "X")] // <-------- X is fully generic
// The automatic where clause ensures X: Display
// We could also use #[delegate(Shout<& 'a str>, generics = "'a")] to only delegate for &str
pub struct WrappedCat(Cat);
对于远程特质:#[delegatable_trait_remote]
如果您想将您crate外部的现有特质使其可用于委托,可以通过将它的签名复制粘贴到您的代码中并使用 #[delegatable_trait_remote]
属性来实现(参见完整代码示例)
use ambassador::delegatable_trait_remote;
use std::fmt::Display;
#[delegatable_trait_remote]
trait Display {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error>;
}
#[derive(Delegate)]
#[delegate(Display)] // <-------- Delegate implementation of Display to struct field
pub struct WrappedCat(Cat);
对于远程类型 #[delegate_remote]
如果您想将您crate外部的现有类型使其可用于委托,可以通过将它的定义复制粘贴到您的代码中并使用 #[delegate_remote]
属性来实现(参见完整代码示例)
如果类型是结构体,不一定所有字段都必须是公共的,只需要被委托的字段即可。
mod wrapped {
use super::*;
pub struct WrappedAnimals<A> {
pub foo: Cat,
pub bar: A,
baz: u32, // private field
}
}
use wrapped::*;
#[delegate_remote]
#[delegate(Shout, target = "bar")]
struct WrappedAnimals<A: Shout> {
foo: Cat,
bar: A,
// We don't even have to include baz since we don't delegate to it
}
注意:由于孤儿规则,#[delegatable_trait_remote]
和 #[delegate_remote]
不能组合使用
使用示例
在这个例子中,我们有一个特质 Shout
,它为 Cat
和 Dog
都实现了。
委托到枚举变体
现在我们添加一个 Animal
枚举,并添加一个委托的特质实现 Shout
,它调用枚举变体的相应实现
use ambassador::{delegatable_trait, Delegate};
#[delegatable_trait]
pub trait Shout {
fn shout(&self, input: &str) -> String;
}
pub struct Cat;
impl Shout for Cat {
fn shout(&self, input: &str) -> String {
format!("{} - meow!", input)
}
}
pub struct Dog;
impl Shout for Dog {
fn shout(&self, input: &str) -> String {
format!("{} - wuff!", input)
}
}
#[derive(Delegate)]
#[delegate(Shout)]
pub enum Animal {
Cat(Cat),
Dog(Dog),
}
pub fn main() {
let foo_animal = Animal::Cat(Cat);
println!("{}", foo_animal.shout("BAR"));
}
委托到元组结构体字段
委托一个元组结构体的特质实现(目前仅支持单字段元组),例如新类型模式。
use ambassador::{delegatable_trait, Delegate};
#[delegatable_trait]
pub trait Shout {
fn shout(&self, input: &str) -> String;
}
pub struct Cat;
impl Shout for Cat {
fn shout(&self, input: &str) -> String {
format!("{} - meow!", input)
}
}
#[derive(Delegate)]
#[delegate(Shout)]
pub struct WrappedCat(Cat);
pub fn main() {
let foo_animal = WrappedCat(Cat);
println!("{}", foo_animal.shout("BAR"));
}
委托到结构体字段
委托一个普通结构的特质实现
use ambassador::{delegatable_trait, Delegate};
#[delegatable_trait]
pub trait Shout {
fn shout(&self, input: &str) -> String;
}
pub struct Cat;
impl Shout for Cat {
fn shout(&self, input: &str) -> String {
format!("{} - meow!", input)
}
}
#[derive(Delegate)]
#[delegate(Shout)]
pub struct WrappedCat {
inner_cat: Cat,
}
pub fn main() {
let foo_animal = WrappedCat { inner_cat: Cat };
println!("{}", foo_animal.shout("BAR"));
}
许可证
根据您的选择,在Apache许可证,版本2.0或MIT许可证下许可。除非您明确表示,否则根据Apache-2.0许可证定义,您有意提交的任何贡献,均应按上述方式双重许可,无需任何额外条款或条件。
依赖项
~2MB
~44K SLoC