4 releases
0.1.3 | Oct 19, 2023 |
---|---|
0.1.2 | Sep 16, 2023 |
0.1.1 | May 12, 2023 |
0.1.0 | Nov 13, 2022 |
#2133 in Procedural macros
Used in parsy
37KB
906 lines
Perfect Derive
Adds derive macros for better bounds on generated Copy, Debug, etc. implementations.
See this blog post for a summary of the issue.
Since Rust cannot handle cyclic bounds, these macros won't always work. Ideally, in a few years this crate can become a no-op and there will be some way to do this in plain Rust, but until then this hack helps clean up some code.
The Issue
Taken from Niko's blog above:
#[derive(Clone)]
struct List<T> {
data: Rc<T>,
next: Option<Rc<List<T>>>,
}
impl<T> Deref for List<T> {
type Target = T;
fn deref(&self) -> &T { &self.data }
}
Currently, derive is going to generate an impl that requires T: Clone
, like this…
impl<T> Clone for List<T>
where
T: Clone,
{
fn clone(&self) {
List {
value: self.value.clone(),
next: self.next.clone(),
}
}
}
The T: Clone
requirement here is not actually necessary. This is because the only T
in this struct is inside of an Rc
, and hence is reference counted. Cloning the Rc
only increments the reference count, it doesn’t actually create a new T
.
With perfect derive, we can do the following instead:
#[perfect_derive(Clone)]
struct List<T> { /* as before */ }
Which generates "better" bounds on the implementation:
impl<T> Clone for List<T>
where
Rc<T>: Clone, // type of the `value` field
Option<Rc<List<T>>: Clone, // type of the `next` field
{
fn clone(&self) { /* as before */ }
}
Note that these bounds do not require that T
is itself clonable.
Dependencies
~0.4–0.9MB
~20K SLoC