#impl-block #generics #impl #type #traits

autogen

Autogen是一组宏,允许您自动将泛型应用到impl块中

5个版本

0.2.3 2024年8月1日
0.2.2 2024年7月31日
0.2.1 2024年7月30日
0.2.0 2024年7月29日
0.1.0 2024年7月21日

#203 in 进程宏

Download history 125/week @ 2024-07-21 447/week @ 2024-07-28 19/week @ 2024-08-04

591 每月下载量

MIT 协议

24KB
289

autogen

github crates.io docs.rs

厌倦了在每个impl块中重复所有泛型吗?

Autogen是一组宏,允许您自动将泛型应用到impl块中。

  • #[register]宏注册了结构体或枚举的泛型,包括生命周期和where子句。
  • #[apply]宏将泛型应用到impl块中。
#[autogen::register]
struct Struct<'a, T, R: ?Sized>
where
    T: PartialEq,
{
    x: T,
    y: &'a R,
}

#[autogen::apply]
impl Struct {}

这将展开为

impl<'a, T, R: ?Sized> Struct<'a, T, R> where T: PartialEq {}

除了类型本身,泛型还可以应用到

  • 给定类型的数组
  • 给定类型的切片
  • 给定类型的引用
  • 给定类型的指针
  • 包含给定类型的元组
  • 包含给定类型作为泛型参数的其他类型
  • 上述所有组合

唯一限制是impl块不能包含不同的已注册类型。

#[autogen::register]
struct Struct<'a, T, R: ?Sized>
where
    T: PartialEq,
{
    x: T,
    y: &'a R,
}

#[autogen::apply]
impl Struct {
    fn x_equals(&self, other: &T) -> bool {
        &self.x == other
    }
    fn y(&self) -> &'a R {
        self.y
    }
}

trait Trait {
    fn type_of(&self) -> &'static str;
}

#[autogen::apply]
impl Trait for Struct {
    fn type_of(&self) -> &'static str {
        "regular"
    }
}

#[autogen::apply]
impl Trait for [Struct; 2] {
    fn type_of(&self) -> &'static str {
        "array of size 2"
    }
}

#[autogen::apply]
impl Trait for [Struct] {
    fn type_of(&self) -> &'static str {
        "slice"
    }
}

#[autogen::apply]
impl Trait for &Struct {
    fn type_of(&self) -> &'static str {
        "reference"
    }
}

#[autogen::apply]
impl Trait for *const Struct {
    fn type_of(&self) -> &'static str {
        "pointer"
    }
}

#[autogen::apply]
impl Trait for (Struct, &'static str, Struct) {
    fn type_of(&self) -> &'static str {
        "tuple"
    }
}

#[autogen::apply]
impl Trait for Result<Struct, String> {
    fn type_of(&self) -> &'static str {
        "generic argument"
    }
}

#[autogen::apply]
impl Trait for [([Option<&Struct>; 1], Struct, String)] {
    fn type_of(&self) -> &'static str {
        "crazy ****"
    }
}

let struct1 = Struct { x: 1, y: "abc" };
assert!(struct1.x_equals(&1));
assert_eq!(struct1.y(), "abc");

assert_eq!(struct1.type_of(), "regular");
assert_eq!((&&struct1).type_of(), "reference");

let pointer = &struct1 as *const Struct<'_, i32, str>;
assert_eq!(pointer.type_of(), "pointer");

let struct2 = Struct { x: 2, y: "xyz" };
assert!(struct2.x_equals(&2));
assert_eq!(struct2.y(), "xyz");

let tuple = (struct1, "string", struct2);
assert_eq!(tuple.type_of(), "tuple");

let array = [tuple.0, tuple.2];
assert_eq!(array.type_of(), "array of size 2");
assert_eq!(array[..].type_of(), "slice");

let struct3 = Struct { x: -1, y: "b" };
let result: Result<_, String> = Ok(struct3);
assert_eq!(result.type_of(), "generic argument");

let struct3 = result.unwrap();
let struct4 = Struct { x: -2, y: "s" };
let crazy = [([Some(&struct3)], struct4, "****".to_string())];
assert_eq!(crazy[..].type_of(), "crazy ****");

默认情况下,泛型会与结构体/枚举名称一起注册,但您也可以提供自定义标识符。如果您在其他模块中已注册具有相同名称的类型,这可能很有用。

这不会编译,因为Name已经被注册。

#[autogen::register]
struct Name<T: PartialEq> {
    t: T,
}

mod sub {
    #[autogen::register]
    pub struct Name<S: FromStr> {
        s: S,
    }
}

为了解决这个问题,您必须提供自定义标识符。

#[autogen::register]
struct Name<T: PartialEq> {
    t: T,
}

#[autogen::apply]
impl Name {
    fn t(&self) -> &T {
        &self.t
    }
}

mod sub {
    use std::str::FromStr;

    #[autogen::register(CustomName)]
    pub struct Name<S: FromStr> {
        s: S,
    }

    #[autogen::apply(CustomName)]
    impl Name {
        pub fn parse(string: &str) -> Result<Self, S::Err> {
            Ok(Name { s: string.parse()? })
        }
        pub fn s(&self) -> &S {
            &self.s
        }
    }
}

let s1 = Name { t: 64 };
assert_eq!(s1.t(), &64);

let s2 = sub::Name::<u32>::parse("123").unwrap();
assert_eq!(s2.s(), &123);

依赖关系

~280–730KB
~17K SLoC