ITADN
blueglyph/trait_gen
blueglyph/trait_gen · 文件 下载 ZIP
文件最后提交记录最后更新时间
README.md
以下内容由 AI 翻译,如有问题请点此提交 issue 反馈

crate documentation build status crate



'trait-gen' 软件包

该软件包提供了属性宏,用于为参数中指定的所有类型生成附带的实现。它最初是为 trait 实现而设计的,因此得名,但也可用于任何泛型实现。

以下是一个简单的示例:

use trait_gen::trait_gen;

trait MyLog { fn my_log2(self) -> u32; }

#[trait_gen(T -> u8, u16, u32, u64, u128)]
impl MyLog for T {
    fn my_log2(self) -> u32 {
        T::BITS - 1 - self.leading_zeros()
    }
}

组合

trait_gen 也会替换内部属性的内容,因此可以将它们链式使用,并将上述示例扩展到所有 T 类型的引用和智能指针:

#[trait_gen(T -> u8, u16, u32, u64, u128)]
#[trait_gen(U -> &T, &mut T, Box<T>)]
impl MyLog for U {
    /// Logarithm base 2 for `${U}`
    fn my_log2(self) -> u32 {
        MyLog::my_log2(*self)
    }
}

元组、排列与条件生成

当多个参数共享类型列表时(换句话说,当我们需要_带重复的排列_或_元组_时),可以使用更简洁的格式:

#[trait_gen(T, U -> u8, u16, u32)]

在以下示例中,我们还展示了条件属性 trait_gen_if, 它为实现提供了更大的灵活性。该条件具有一般格式 <argument> in <types>,或其否定形式,!<argument> in <types>。当参数与其中一种类型相同时, 代码分别被包含或跳过。

use trait_gen::{trait_gen, trait_gen_if};

#[derive(Clone, PartialEq, Debug)]
struct Wrapper<T>(T);

#[trait_gen(T, U -> u8, u16, u32)]
// The types T and U must be different to avoid the compilation error
// "conflicting implementation in crate `core`: impl<T> From<T> for T"
#[trait_gen_if(!T in U)]
impl From<Wrapper<U>> for Wrapper<T> {
    /// converts Wrapper<${U}> to Wrapper<${T}>
    fn from(value: Wrapper<U>) -> Self {
        Wrapper(T::try_from(value.0)
            .expect(&format!("overflow when converting {} to ${T}", value.0)))
    }
}

这将为我们提供所有从/到 u8u16u32 的转换,除了相同类型之间的转换,因为它们已由标准库中的通用实现所覆盖。trait_gen_if 对于根据实现类型选择常量或移除方法也非常有用。

注意:感谢 Daniel Vigovszky 给我条件生成的想法!他最初在一个名为 conditional_trait_gen 的分支中实现了它,尽管方式大不相同。我在旧帖子中曾考虑过一些需要此功能的使用场景,但直到 1.1.0 版本才着手实现。

上述效果可以通过以下更短的格式实现,该格式生成 TU 的所有类型组合,使得 T != U2-排列):

#[trait_gen(T != U -> u8, u16, u32)]
impl From<Wrapper<U>> for Wrapper<T> { /* ... */ }

其他特性

请阅读 crate 文档 以获取更多详细信息。

动机

还有其他生成多个实现的方式:

  • 手动复制,这很繁琐、容易出错且难以维护
  • 使用声明式宏
  • 使用 blanket implementation

使用 声明式宏 会得到类似以下内容:

macro_rules! impl_my_log {
    ($($t:ty)*) => (
        $(impl MyLog for $t {
            fn my_log2(self) -> u32 {
                $t::BITS - 1 - self.leading_zeros()
            }
        })*
    )
}

impl_my_log! { u8 u16 u32 u64 u128 }

它能完成任务,但比原生代码更难阅读,而且 IDE 通常无法在宏代码中提供上下文帮助或应用重构。当我们查找由声明式宏生成的方法定义时,得到以下代码行也相当令人烦恼且毫无帮助:

impl_my_log! { u8 u16 u32 u64 u128 }

使用通用实现非常强大,但存在其他缺点:

  • 它禁止为同一 crate 中尚未被通用实现覆盖的类型之外的任何类型提供实现,因此仅当实现可以为所有约束类型(包括当前和未来的类型)编写时才有效。
  • 并不总能找到与我们需要编写的代码相对应的 trait。例如,num crate 为基本类型提供了大量帮助,但并非所有内容都被覆盖。
  • 为整个类型范围进行实现并不总是可取的。
  • 即使操作和常量由 trait 覆盖,也很快需要一长串 trait 约束。

我认为,由于语言本身缺乏为类型列表实现通用代码的默认方式,过程宏是次优选择。

兼容性

trait-gen crate 在 Windows 64 位和 Linux 64/32 位平台上针对 Rust 版本 1.61.0 及稳定版进行了测试。

发布

RELEASES.md 记录了所有发布(大多数也在 GitHub release page 上)。

许可证

本代码根据 MIT LicenseApache License 2.0 进行许可,供您选择。