derive-where
描述
一个用于简化派生标准及其他 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 边界。
下面的示例将 Example 的 Clone 实现
限制为 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>),
}
跳过字段
使用 skip 或 skip_inner 属性,可以跳过允许此操作的 trait 中的字段,这些 trait 包括:Debug、Hash、Ord、PartialOrd、
PartialEq、Zeroize 和 ZeroizeOnDrop。
#[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,也可以选择性地跳过字段,这在
skip 和 skip_inner 中均适用。为了防止破坏为这些
trait 定义的不变量,其中一些只能成组跳过。可用的组包括:
Clone:使用Default代替Clone。DebugEqHashOrd:跳过Eq、Hash、Ord、PartialOrd和PartialEq。HashZeroize:跳过Zeroize和ZeroizeOnDrop。
#[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 可用于在 PartialEq 和 PartialOrd 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));
请注意,无法将 incomparable 与 Eq 或 Ord 一起使用,
因为这会破坏它们的不变量。
Serde Deserialize 和 Serialize
派生 Deserialize 和 Serialize 按预期工作。虽然
derive-where 不提供任何属性选项,但可以使用常规的 serde 属性。
Derive-where 将遵循
#[serde(crate = "...")]。
Zeroize 选项
Zeroize 有两个选项:
crate:一个项级选项,用于在重新导出或重命名的情况下指定zeroizecrate 的路径。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: 一个项级选项,用于在重新导出或重命名的情况下指定zeroizecrate 的路径。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 派生:
CloneCopyDebugDefaultDeserialize:仅在启用serdecrate 特性时可用。EqHashOrdPartialEqPartialOrdSerialize:仅在启用serdecrate 特性时可用。Zeroize:仅在启用zeroizecrate 特性时可用。ZeroizeOnDrop:仅在启用zeroizecrate 特性时可用。如果 启用了zeroize-on-drop特性,则实现ZeroizeOnDrop, 否则仅实现Drop。
支持的项目
支持结构体、元组结构体、联合体和枚举。Derive-where 会尽力
阻止使用 std 的 derive 可以覆盖的用法。例如,
不支持单元结构体和仅包含单元变体的枚举。
no_std 支持
默认提供 no_std 支持。
Crate 特性
nightly:借助core::intrinsics::discriminant_value实现Ord和PartialOrd, 这也是 Rust 的默认行为。这需要 Rust 编译器的 nightly 版本。safe:safe:仅使用安全方式访问Ord和PartialOrd中枚举的判别式。 它还用unreachable替换了Ord、PartialEq和PartialOrd中所有core::hint::unreachable_unchecked的用法,这也是 std 所使用的。zeroize:允许在Drop上派生Zeroize和zeroize。zeroize-on-drop:允许派生Zeroize和ZeroizeOnDrop, 并且要求zeroizev1.5。
MSRV
当前的 MSRV 为 1.57,并由 CI 进行检查。任何变更都将伴随一个次要版本号的提升。如果 MSRV 对您很重要,请使用
derive-where = "~1.x" 将您的 crate 固定到特定的次要版本。
替代方案
- derivative
是一个拥有许多选项的优秀替代方案。值得注意的是,它不支持
no_std,并且使用时需要额外的#[derive(Derivative)]。 - derive_bounded
是一个仍在开发中的新替代方案。
变更日志
有关详细信息,请参阅 CHANGELOG 文件。
许可证
根据以下任一许可证授权:
- Apache License, Version 2.0 (LICENSE-APACHE 或 http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT 或 http://opensource.org/licenses/MIT)
供您选择。
贡献
除非您明确另有说明,否则您有意提交以包含在本作品中的任何贡献,如 Apache-2.0 许可证中所定义,将按照上述双重许可进行授权,不附加任何额外条款或 条件。