perfect-derive

Crates.ioperfect-derive
lib.rsperfect-derive
version0.1.3
sourcesrc
created_at2022-11-13 12:59:46.230484
updated_at2023-10-19 14:30:49.695737
descriptionProvides a prototype of the proposed perfect_derive macro
homepagehttps://github.com/LucentFlux/perfect-derive
repositoryhttps://github.com/LucentFlux/perfect-derive
max_upload_size
id714184
size43,954
Joe O'Connor (Joeoc2001)

documentation

README

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.

Commit count: 18

cargo fmt