#macro-derive #macro #derive #utility #debugging

macro perfect-derive

Provides a prototype of the proposed perfect_derive macro

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

MIT license

37KB
906 lines

Perfect Derive

crates.io docs.rs crates.io

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