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

derive-where

Crates.io Version Live Build Status Docs.rs Documentation

描述

一个用于简化派生标准及其他 trait 的 proc-macro,支持自定义泛型类型约束。

用法

derive_where 属性可以像 std 的 #[derive(...)] 语句一样使用:

#[derive_where(Clone, Debug)]
struct Example<T>(PhantomData<T>);

这将为任何 T 生成 Example 的 trait 实现, 而 std 的派生只会将 T: Trait 绑定到对应的 trait 来实现这些 trait。

可以为一个 项添加多个 derive_where 属性,但只有第一个必须使用任何路径限定。

#[derive_where::derive_where(Clone, Debug)]
#[derive_where(Eq, PartialEq)]
struct Example1<T>(PhantomData<T>);

如果使用不同的包名,必须指定此项:

#[derive_where(crate = derive_where_)]
#[derive_where(Clone, Debug)]
struct Example<T>(PhantomData<T>);

此外,还提供以下便捷选项:

通用类型约束

使用分号与 trait 列表分隔,可以指定要约束的类型。此示例将 Example 的实现限制为 T: Clone

#[derive_where(Clone, Debug; T)]
struct Example<T, U>(T, PhantomData<U>);

也可以指定要应用的边界。这将 将 Example 的实现绑定到 T: Super

trait Super: Clone + Debug {}

#[derive_where(Clone, Debug; T: Super)]
struct Example<T>(PhantomData<T>);

但同样可以实现更复杂的 trait 边界。 下面的示例将 ExampleClone 实现 限制为 T::Type: Clone

trait Trait {
	type Type;
}

struct Impl;

impl Trait for Impl {
	type Type = i32;
}

#[derive_where(Clone, Debug; T::Type)]
struct Example<T: Trait>(T::Type);

此处列出的任意选项组合均可用于满足特定约束。在需要时,也可以使用多个独立的约束规范:

#[derive_where(Clone, Debug; T)]
#[derive_where(Eq, PartialEq; U)]
struct Example<T, U>(PhantomData<T>, PhantomData<U>);

枚举默认值

自 Rust 1.62 起,可以通过 #[default] 属性在枚举上派生 Default。Derive-where 允许通过 #[derive_where(default)] 属性实现此功能:

#[derive_where(Clone, Default)]
enum Example<T> {
	#[derive_where(default)]
	A(PhantomData<T>),
}

跳过字段

使用 skipskip_inner 属性,可以跳过允许此操作的 trait 中的字段,这些 trait 包括:DebugHashOrdPartialOrdPartialEqZeroizeZeroizeOnDrop

#[derive_where(Debug, PartialEq; T)]
struct Example<T>(#[derive_where(skip)] T);

assert_eq!(format!("{:?}", Example(42)), "Example");
assert_eq!(Example(42), Example(0));

如果希望,也可以跳过项目或变体中的所有字段:

#[derive_where(Debug, PartialEq)]
#[derive_where(skip_inner)]
struct StructExample<T>(T);

assert_eq!(format!("{:?}", StructExample(42)), "StructExample");
assert_eq!(StructExample(42), StructExample(0));

#[derive_where(Debug, PartialEq)]
enum EnumExample<T> {
	#[derive_where(skip_inner)]
	A(T),
}

assert_eq!(format!("{:?}", EnumExample::A(42)), "A");
assert_eq!(EnumExample::A(42), EnumExample::A(0));

对于某些 trait,也可以选择性地跳过字段,这在 skipskip_inner 中均适用。为了防止破坏为这些 trait 定义的不变量,其中一些只能成组跳过。可用的组包括:

#[derive_where(Debug, PartialEq)]
#[derive_where(skip_inner(Debug))]
struct Example<T>(i32, PhantomData<T>);

assert_eq!(format!("{:?}", Example(42, PhantomData::<()>)), "Example");
assert_ne!(
	Example(42, PhantomData::<()>),
	Example(0, PhantomData::<()>)
);

不可比较的变体/项

skip 属性类似,incomparable 可用于在 PartialEqPartialOrd trait 实现中跳过变体 或项,这意味着对于 eq 它们始终返回 false,对于 partial_cmp 始终返回 None。这 导致除 != 外的所有比较,即 ==<<=>=>, 与标记的变体或结构体比较时均求值为 false

# use derive_where::derive_where;
#[derive(Debug)]
#[derive_where(PartialEq, PartialOrd)]
enum EnumExample {
	#[derive_where(incomparable)]
	Incomparable,
	Comparable,
}
assert_eq!(EnumExample::Comparable, EnumExample::Comparable);
assert_ne!(EnumExample::Incomparable, EnumExample::Incomparable);
assert!(!(EnumExample::Comparable >= EnumExample::Incomparable));
assert!(!(EnumExample::Comparable <= EnumExample::Incomparable));
assert!(!(EnumExample::Incomparable >= EnumExample::Incomparable));
assert!(!(EnumExample::Incomparable <= EnumExample::Incomparable));

#[derive(Debug)]
#[derive_where(PartialEq, PartialOrd)]
#[derive_where(incomparable)]
struct StructExample;

assert_ne!(StructExample, StructExample);
assert!(!(StructExample >= StructExample));
assert!(!(StructExample <= StructExample));

请注意,无法将 incomparableEqOrd 一起使用, 因为这会破坏它们的不变量。

Serde DeserializeSerialize

派生 DeserializeSerialize 按预期工作。虽然 derive-where 不提供任何属性选项,但可以使用常规的 serde 属性。 Derive-where 将遵循 #[serde(crate = "...")]

Zeroize 选项

Zeroize 有两个选项:

  • crate:一个项级选项,用于在重新导出或重命名的情况下指定 zeroize crate 的路径。
  • fqs:一个字段级选项,将使用完全限定语法,而不是 直接在 self 上调用 zeroize 方法。这是 为了避免与另一个同样名为 zeroize 的方法产生歧义。
#[derive_where(Zeroize(crate = zeroize_))]
struct Example(#[derive_where(Zeroize(fqs))] i32);

impl Example {
	// If we didn't specify the `fqs` option, this would lead to a compile
	// error because of method ambiguity.
	fn zeroize(&mut self) {
		self.0 = 1;
	}
}

let mut test = Example(42);

// Will call the struct method.
test.zeroize();
assert_eq!(test.0, 1);

// WIll call the `Zeroize::zeroize` method.
Zeroize::zeroize(&mut test);
assert_eq!(test.0, 0);

ZeroizeOnDrop 选项

如果启用了 zeroize-on-drop 特性,它将实现 ZeroizeOnDrop 并且可以在没有 Zeroize 的情况下实现,否则它仅实现 Drop 并且要求实现 Zeroize

ZeroizeOnDrop 有两个选项:

  • crate: 一个项级选项,用于在重新导出或重命名的情况下指定 zeroize crate 的路径。
  • no_drop: 一个项级选项,它将不实现 Drop,而是仅断言 每个字段都实现了 ZeroizeOnDrop。需要 zeroize-on-drop 特性。
#[derive_where(ZeroizeOnDrop(crate = zeroize_))]
struct Example(i32);

assert!(core::mem::needs_drop::<Example>());

支持的 trait

以下 trait 可以通过 derive-where 派生:

支持的项目

支持结构体、元组结构体、联合体和枚举。Derive-where 会尽力 阻止使用 std 的 derive 可以覆盖的用法。例如, 不支持单元结构体和仅包含单元变体的枚举。

联合体仅支持 CloneCopy

no_std 支持

默认提供 no_std 支持。

Crate 特性

MSRV

当前的 MSRV 为 1.57,并由 CI 进行检查。任何变更都将伴随一个次要版本号的提升。如果 MSRV 对您很重要,请使用 derive-where = "~1.x" 将您的 crate 固定到特定的次要版本。

替代方案

  • derivative Crates.io 是一个拥有许多选项的优秀替代方案。值得注意的是,它不支持 no_std,并且使用时需要额外的 #[derive(Derivative)]
  • derive_bounded Crates.io 是一个仍在开发中的新替代方案。

变更日志

有关详细信息,请参阅 CHANGELOG 文件。

许可证

根据以下任一许可证授权:

供您选择。

贡献

除非您明确另有说明,否则您有意提交以包含在本作品中的任何贡献,如 Apache-2.0 许可证中所定义,将按照上述双重许可进行授权,不附加任何额外条款或 条件。