Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/modprog/derive-where
Attribute proc-macro to simplify deriving standard and other traits with custom generic type bounds.
https://github.com/modprog/derive-where
derive macros rust
Last synced: 5 days ago
JSON representation
Attribute proc-macro to simplify deriving standard and other traits with custom generic type bounds.
- Host: GitHub
- URL: https://github.com/modprog/derive-where
- Owner: ModProg
- License: apache-2.0
- Created: 2021-10-30T12:00:14.000Z (about 3 years ago)
- Default Branch: main
- Last Pushed: 2024-12-07T07:32:55.000Z (about 2 months ago)
- Last Synced: 2025-01-13T11:03:17.859Z (12 days ago)
- Topics: derive, macros, rust
- Language: Rust
- Homepage:
- Size: 556 KB
- Stars: 66
- Watchers: 3
- Forks: 4
- Open Issues: 10
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE-APACHE
Awesome Lists containing this project
README
# derive-where
[![Crates.io Version](https://img.shields.io/crates/v/derive-where.svg)](https://crates.io/crates/derive-where)
[![Live Build Status](https://img.shields.io/github/actions/workflow/status/ModProg/derive-where/test.yml?branch=main)](https://github.com/ModProg/derive-where/actions/workflows/test.yml)
[![Docs.rs Documentation](https://img.shields.io/docsrs/derive-where)](https://docs.rs/crate/derive-where)## Description
Attribute proc-macro to simplify deriving standard and other traits with
custom generic type bounds.## Usage
The [`derive_where`] attribute can be used just like
std's `#[derive(...)]` statements:```rust
#[derive_where(Clone, Debug)]
struct Example(PhantomData);
```This will generate trait implementations for `Example` for any `T`,
as opposed to std's derives, which would only implement these traits with
`T: Trait` bound to the corresponding trait.Multiple [`derive_where`] attributes can be added to an
item, but only the first one must use any path qualifications.```rust
#[derive_where::derive_where(Clone, Debug)]
#[derive_where(Eq, PartialEq)]
struct Example1(PhantomData);
```If using a different package name, you must specify this:
```rust
#[derive_where(crate = derive_where_)]
#[derive_where(Clone, Debug)]
struct Example(PhantomData);
```In addition, the following convenience options are available:
### Generic type bounds
Separated from the list of traits with a semi-colon, types to bind to can be
specified. This example will restrict the implementation for `Example` to
`T: Clone`:```rust
#[derive_where(Clone, Debug; T)]
struct Example(T, PhantomData);
```It is also possible to specify the bounds to be applied. This will
bind implementation for `Example` to `T: Super`:```rust
trait Super: Clone + Debug {}#[derive_where(Clone, Debug; T: Super)]
struct Example(PhantomData);
```But more complex trait bounds are possible as well.
The example below will restrict the [`Clone`] implementation for `Example`
to `T::Type: Clone`:```rust
trait Trait {
type Type;
}struct Impl;
impl Trait for Impl {
type Type = i32;
}#[derive_where(Clone, Debug; T::Type)]
struct Example(T::Type);
```Any combination of options listed here can be used to satisfy a
specific constrain. It is also possible to use multiple separate
constrain specifications when required:```rust
#[derive_where(Clone, Debug; T)]
#[derive_where(Eq, PartialEq; U)]
struct Example(PhantomData, PhantomData);
```### Enum default
Since Rust 1.62 deriving [`Default`] on an enum is possible with the
`#[default]` attribute. Derive-where allows this with a
`#[derive_where(default)]` attribute:```rust
#[derive_where(Clone, Default)]
enum Example {
#[derive_where(default)]
A(PhantomData),
}
```### Skipping fields
With a `skip` or `skip_inner` attribute fields can be skipped for traits
that allow it, which are: [`Debug`], [`Hash`], [`Ord`], [`PartialOrd`],
[`PartialEq`], [`Zeroize`] and [`ZeroizeOnDrop`].```rust
#[derive_where(Debug, PartialEq; T)]
struct Example(#[derive_where(skip)] T);assert_eq!(format!("{:?}", Example(42)), "Example");
assert_eq!(Example(42), Example(0));
```It is also possible to skip all fields in an item or variant if desired:
```rust
#[derive_where(Debug, PartialEq)]
#[derive_where(skip_inner)]
struct StructExample(T);assert_eq!(format!("{:?}", StructExample(42)), "StructExample");
assert_eq!(StructExample(42), StructExample(0));#[derive_where(Debug, PartialEq)]
enum EnumExample {
#[derive_where(skip_inner)]
A(T),
}assert_eq!(format!("{:?}", EnumExample::A(42)), "A");
assert_eq!(EnumExample::A(42), EnumExample::A(0));
```Selective skipping of fields for certain traits is also an option, both in
`skip` and `skip_inner`. To prevent breaking invariants defined for these
traits, some of them can only be skipped in groups. The following groups are
available:
- [`Debug`]
- `EqHashOrd`: Skips [`Eq`], [`Hash`], [`Ord`], [`PartialOrd`] and
[`PartialEq`].
- [`Hash`]
- `Zeroize`: Skips [`Zeroize`] and [`ZeroizeOnDrop`].```rust
#[derive_where(Debug, PartialEq)]
#[derive_where(skip_inner(Debug))]
struct Example(i32, PhantomData);assert_eq!(format!("{:?}", Example(42, PhantomData::<()>)), "Example");
assert_ne!(
Example(42, PhantomData::<()>),
Example(0, PhantomData::<()>)
);
```### Incomparable variants/items
Similar to the `skip` attribute, `incomparable` can be used to skip variants
or items in [`PartialEq`] and [`PartialOrd`] trait implementations, meaning
they will always yield `false` for `eq` and `None` for `partial_cmp`. This
results in all comparisons but `!=`, i.e. `==`, `<`, `<=`, `>=` and `>`,
with the marked variant or struct evaluating to `false`.```rust
# 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));
```Note that it is not possible to use `incomparable` with [`Eq`] or [`Ord`] as
that would break their invariants.### `Zeroize` options
`Zeroize` has two options:
- `crate`: an item-level option which specifies a path to the [`zeroize`]
crate in case of a re-export or rename.
- `fqs`: a field-level option which will use fully-qualified-syntax instead
of calling the [`zeroize`][method@zeroize] method on `self` directly. This
is to avoid ambiguity between another method also called `zeroize`.```rust
#[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` options
If the `zeroize-on-drop` feature is enabled, it implements [`ZeroizeOnDrop`]
and can be implemented without [`Zeroize`], otherwise it only implements
[`Drop`] and requires [`Zeroize`] to be implemented.[`ZeroizeOnDrop`] has one option:
- `crate`: an item-level option which specifies a path to the [`zeroize`]
crate in case of a re-export or rename.```rust
#[derive_where(ZeroizeOnDrop(crate = zeroize_))]
struct Example(i32);assert!(core::mem::needs_drop::());
```### Supported traits
The following traits can be derived with derive-where:
- [`Clone`]
- [`Copy`]
- [`Debug`]
- [`Default`]
- [`Eq`]
- [`Hash`]
- [`Ord`]
- [`PartialEq`]
- [`PartialOrd`]
- [`Zeroize`]: Only available with the `zeroize` crate feature.
- [`ZeroizeOnDrop`]: Only available with the `zeroize` crate feature. If the
`zeroize-on-drop` feature is enabled, it implements [`ZeroizeOnDrop`],
otherwise it only implements [`Drop`].### Supported items
Structs, tuple structs, unions and enums are supported. Derive-where tries
it's best to discourage usage that could be covered by std's `derive`. For
example unit structs and enums only containing unit variants aren't
supported.Unions only support [`Clone`] and [`Copy`].
[`PartialOrd`] and [`Ord`] need to determine the discriminant type to
function correctly. To protect against a potential future change to the
default discriminant type, some compile-time validation is inserted to
ascertain that the type remains `isize`.### `no_std` support
`no_std` support is provided by default.
## Crate features
- `nightly`: Implements [`Ord`] and [`PartialOrd`] with the help of
[`core::intrinsics::discriminant_value`], which is what Rust does by
default too. This requires a nightly version of the Rust compiler.
- `safe`: `safe`: Uses only safe ways to access the discriminant of the enum
for [`Ord`] and [`PartialOrd`]. It also replaces all cases of
[`core::hint::unreachable_unchecked`] in [`Ord`], [`PartialEq`] and
[`PartialOrd`], which is what std uses, with [`unreachable`].
- `zeroize`: Allows deriving [`Zeroize`] and [`zeroize`][method@zeroize] on
[`Drop`].
- `zeroize-on-drop`: Allows deriving [`Zeroize`] and [`ZeroizeOnDrop`] and
requires [`zeroize`] v1.5.## MSRV
The current MSRV is 1.57 and is being checked by the CI. A change will be
accompanied by a minor version bump. If MSRV is important to you, use
`derive-where = "~1.x"` to pin a specific minor version to your crate.## Alternatives
- [derivative](https://crates.io/crates/derivative) [![Crates.io](https://img.shields.io/crates/v/derivative.svg)](https://crates.io/crates/derivative)
is a great alternative with many options. Notably it doesn't support
`no_std` and requires an extra `#[derive(Derivative)]` to use.
- [derive_bounded](https://crates.io/crates/derive_bounded) [![Crates.io](https://img.shields.io/crates/v/derive_bounded.svg)](https://crates.io/crates/derive_bounded)
is a new alternative still in development.## Changelog
See the [CHANGELOG] file for details.
## License
Licensed under either of
- Apache License, Version 2.0 ([LICENSE-APACHE] or )
- MIT license ([LICENSE-MIT] or )at your option.
### Contribution
Unless you explicitly state otherwise, any contribution intentionally
submitted for inclusion in the work by you, as defined in the Apache-2.0
license, shall be dual licensed as above, without any additional terms or
conditions.[CHANGELOG]: https://github.com/ModProg/derive-where/blob/main/CHANGELOG.md
[LICENSE-MIT]: https://github.com/ModProg/derive-where/blob/main/LICENSE-MIT
[LICENSE-APACHE]: https://github.com/ModProg/derive-where/blob/main/LICENSE-APACHE
[`Debug`]: https://doc.rust-lang.org/core/fmt/trait.Debug.html
[`Default`]: https://doc.rust-lang.org/core/default/trait.Default.html
[`Hash`]: https://doc.rust-lang.org/core/hash/trait.Hash.html
[`zeroize`]: https://docs.rs/zeroize
[`Zeroize`]: https://docs.rs/zeroize/latest/zeroize/trait.Zeroize.html
[`ZeroizeOnDrop`]: https://docs.rs/zeroize/latest/zeroize/trait.ZeroizeOnDrop.html
[method@zeroize]: https://docs.rs/zeroize/latest/zeroize/trait.Zeroize.html#tymethod.zeroize[`Clone`]: https://doc.rust-lang.org/core/clone/trait.Clone.html
[`Copy`]: https://doc.rust-lang.org/core/marker/trait.Copy.html
[`core::hint::unreachable_unchecked`]: https://doc.rust-lang.org/core/hint/fn.unreachable_unchecked.html
[`core::intrinsics::discriminant_value`]: https://doc.rust-lang.org/core/intrinsics/fn.discriminant_value.html
[`derive_where`]: https://docs.rs/derive-where/latest/derive_where/attr.derive_where.html
[`Discriminant`]: https://doc.rust-lang.org/core/mem/struct.Discriminant.html
[`Drop`]: https://doc.rust-lang.org/core/ops/trait.Drop.html
[`Eq`]: https://doc.rust-lang.org/core/cmp/trait.Eq.html
[`i32`]: https://doc.rust-lang.org/core/primitive.i32.html
[`isize`]: https://doc.rust-lang.org/core/primitive.isize.html
[`Ord`]: https://doc.rust-lang.org/core/cmp/trait.Ord.html
[`PartialEq`]: https://doc.rust-lang.org/core/cmp/trait.PartialEq.html
[`PartialOrd`]: https://doc.rust-lang.org/core/cmp/trait.PartialOrd.html
[`transmute`]: https://doc.rust-lang.org/core/mem/fn.transmute.html
[`unreachable`]: https://doc.rust-lang.org/core/macro.unreachable.html