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过程宏

Download history 4180/week @ 2024-05-04 3715/week @ 2024-05-11 2698/week @ 2024-05-18 2499/week @ 2024-05-25 3336/week @ 2024-06-01 3891/week @ 2024-06-08 3335/week @ 2024-06-15 2898/week @ 2024-06-22 4221/week @ 2024-06-29 3507/week @ 2024-07-06 4715/week @ 2024-07-13 4064/week @ 2024-07-20 3714/week @ 2024-07-27 5224/week @ 2024-08-03 3168/week @ 2024-08-10 2839/week @ 2024-08-17

15,576 每月下载量
用于 30 个crate(直接使用18个)

MIT/Apache

83KB
1K SLoC

Ambassador - 通过过程宏委托特质实现

Crates.io version docs.rs docs

通常,将特质的实现委托给枚举变体或结构体的字段需要大量的样板代码。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,它为 CatDog 都实现了。

委托到枚举变体

现在我们添加一个 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.0MIT许可证下许可。
除非您明确表示,否则根据Apache-2.0许可证定义,您有意提交的任何贡献,均应按上述方式双重许可,无需任何额外条款或条件。

依赖项

~2MB
~44K SLoC