7 个版本 (4 个稳定)
1.2.7 | 2023 年 12 月 14 日 |
---|---|
1.2.5 | 2023 年 9 月 3 日 |
1.2.1 |
|
1.2.0 |
|
1.0.0-rc.1 | 2021 年 12 月 8 日 |
#107 在 Rust 模式
409,006 每月下载量
用于 258 个crate(24 个直接使用)
270KB
8K SLoC
derive-where
描述
属性 proc-macro 用于简化使用自定义泛型类型界限派生标准和其它特质。
用法
可以像 std 的 derive
语句一样使用 derive_where
属性。
#[derive_where(Clone, Debug)]
struct Example<T>(PhantomData<T>);
这将为任何 T
生成 Example
的特质实现,而 std 的 derives 只会在 T : 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>);
此外,以下便捷选项也可用
泛型类型界限
使用分号从特质列表中分离,可以指定要绑定到的类型。此示例将 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>);
但也可以指定更复杂的特质界限。下面的示例将 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(default)]
属性,Derive-where允许这样做。
#[derive_where(Clone, Default)]
enum Example<T> {
#[derive_where(default)]
A(PhantomData<T>),
}
跳过字段
使用skip
或skip_inner
属性,可以在允许的情况下跳过某些特质的字段,这些特质包括: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));
对于某些特质,可以选择性地跳过字段,包括在skip
和skip_inner
中。为了避免破坏为这些特质定义的不变量,其中一些只能在组中跳过。以下是一些可用的组:
Debug
EqHashOrd
:跳过Eq
、Hash
、Ord
、PartialOrd
和PartialEq
。Hash
Zeroize
:跳过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
特质实现中跳过变体或项目,这意味着它们在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
一起使用,因为这会破坏它们的约定。
Zeroize
选项
Zeroize
有两个选项
crate
:一个项目级选项,用于指定在重导出或重命名的情况下,到zeroize
包的路径。fqs
:一个字段级选项,将使用完全限定语法而不是直接调用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
包的路径。
#[derive_where(ZeroizeOnDrop(crate = zeroize_))]
struct Example(i32);
assert!(core::mem::needs_drop::<Example>());
支持的特性
以下特性可以使用 derive-where 进行派生
Clone
Copy
Debug
Default
Eq
Hash
Ord
PartialEq
PartialOrd
Zeroize
:仅在使用zeroize
包特性时可用。ZeroizeOnDrop
:仅在使用zeroize
包特性时可用。如果启用zeroize-on-drop
功能,则实现ZeroizeOnDrop
,否则它只实现Drop
。
支持的项目
支持结构体、元组结构体、联合体和枚举。Derive-where 尽量避免使用 std 的 derive
可以覆盖的使用方式。例如,仅包含单元变体的单元结构和枚举不受支持。
PartialOrd
和 Ord
需要确定判别类型才能正确工作。为了防止未来对默认判别类型的潜在更改,插入了一些编译时验证来确保类型保持为 isize
。
no_std
支持
no_std
支持默认提供。
包特性
nightly
:通过Ord
和PartialOrd
实现了core::intrinsics::discriminant_value
的帮助,这与Rust默认行为相同。这需要Rust编译器的nightly版本。safe
:仅使用安全方式访问枚举的判别式,用于Ord
和PartialOrd
。它还用unreachable
替换了core::hint::unreachable_unchecked
在Ord
、PartialEq
和PartialOrd
中的所有情况,这是std使用的,与unreachable
相同。zeroize
:允许在Drop
上推导Zeroize
和zeroize
。zeroize-on-drop
:允许推导Zeroize
和ZeroizeOnDrop
,并需要zeroize
v1.5。
MSRV
当前的MSRV是1.57,并由CI进行检查。更改将伴随着次要版本的提升。如果您认为MSRV很重要,请使用derive-where = "~1.x"
将特定次要版本锁定到您的crate。
替代方案
- derivative 是一个功能丰富的替代品,具有许多选项。值得注意的是,它不支持
no_std
,并且需要额外的#[derive)]
才能使用。 - derive_bounded 是一个仍在开发中的新替代方案。
更新日志
有关详细信息,请参阅更新日志文件。
许可协议
在以下任一许可协议下获得许可
- Apache License,版本2.0(LICENSE-APACHE或http://www.apache.org/licenses/LICENSE-2.0)
- MIT许可(LICENSE-MIT或http://opensource.org/licenses/MIT)
由您选择。
贡献
除非您明确声明,否则根据Apache-2.0许可证定义的任何有意提交以包含在作品中的贡献,应按上述方式双重许可,不附加任何额外条款或条件。
依赖关系
~320–780KB
~19K SLoC