7 个版本 (4 个稳定)

1.2.7 2023 年 12 月 14 日
1.2.5 2023 年 9 月 3 日
1.2.1 2023 年 4 月 14 日
1.2.0 2023 年 3 月 19 日
1.0.0-rc.12021 年 12 月 8 日

#107Rust 模式

Download history 55422/week @ 2024-03-14 60878/week @ 2024-03-21 70573/week @ 2024-03-28 69712/week @ 2024-04-04 78376/week @ 2024-04-11 78168/week @ 2024-04-18 74804/week @ 2024-04-25 78825/week @ 2024-05-02 82183/week @ 2024-05-09 84108/week @ 2024-05-16 91576/week @ 2024-05-23 108271/week @ 2024-05-30 101344/week @ 2024-06-06 95486/week @ 2024-06-13 91520/week @ 2024-06-20 95268/week @ 2024-06-27

409,006 每月下载量
用于 258 个crate(24 个直接使用)

MIT/Apache

270KB
8K SLoC

derive-where

Crates.io Version Live Build Status Docs.rs Documentation

描述

属性 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>);

但也可以指定更复杂的特质界限。下面的示例将 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(default)]属性,Derive-where允许这样做。

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

跳过字段

使用skipskip_inner属性,可以在允许的情况下跳过某些特质的字段,这些特质包括: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));

对于某些特质,可以选择性地跳过字段,包括在skipskip_inner中。为了避免破坏为这些特质定义的不变量,其中一些只能在组中跳过。以下是一些可用的组:

#[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特质实现中跳过变体或项目,这意味着它们在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 一起使用,因为这会破坏它们的约定。

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 进行派生

支持的项目

支持结构体、元组结构体、联合体和枚举。Derive-where 尽量避免使用 std 的 derive 可以覆盖的使用方式。例如,仅包含单元变体的单元结构和枚举不受支持。

联合体只支持 CloneCopy

PartialOrdOrd 需要确定判别类型才能正确工作。为了防止未来对默认判别类型的潜在更改,插入了一些编译时验证来确保类型保持为 isize

no_std 支持

no_std 支持默认提供。

包特性

  • nightly:通过OrdPartialOrd实现了core::intrinsics::discriminant_value的帮助,这与Rust默认行为相同。这需要Rust编译器的nightly版本。
  • safe:仅使用安全方式访问枚举的判别式,用于OrdPartialOrd。它还用unreachable替换了core::hint::unreachable_uncheckedOrdPartialEqPartialOrd中的所有情况,这是std使用的,与unreachable相同。
  • zeroize:允许在Drop上推导Zeroizezeroize
  • zeroize-on-drop:允许推导ZeroizeZeroizeOnDrop,并需要zeroize v1.5。

MSRV

当前的MSRV是1.57,并由CI进行检查。更改将伴随着次要版本的提升。如果您认为MSRV很重要,请使用derive-where = "~1.x"将特定次要版本锁定到您的crate。

替代方案

  • derivative Crates.io是一个功能丰富的替代品,具有许多选项。值得注意的是,它不支持no_std,并且需要额外的#[derive)]才能使用。
  • derive_bounded Crates.io是一个仍在开发中的新替代方案。

更新日志

有关详细信息,请参阅更新日志文件。

许可协议

在以下任一许可协议下获得许可

由您选择。

贡献

除非您明确声明,否则根据Apache-2.0许可证定义的任何有意提交以包含在作品中的贡献,应按上述方式双重许可,不附加任何额外条款或条件。

依赖关系

~320–780KB
~19K SLoC