Giter VIP home page Giter VIP logo

rust-derivative's Introduction

Derivative

Crates.io Crates.io Continuous integration

This crate provides a set of alternative customizable #[derive] attributes for Rust.

Stability

This crate is stable and follows semver. It requires rustc 1.34 or later and changing the minimal rustc version will be considered a semver breaking change.

What it does

#[derive(Derivative)]
#[derivative(Debug)]
struct Foo {
    foo: u8,
    #[derivative(Debug="ignore")]
    bar: u8,
}

// Prints `Foo { foo: 42 }`
println!("{:?}", Foo { foo: 42, bar: 1 });

Check the documentation for more!

License

Licensed under either of

at your option.

Acknowledgements

This is inspired from how serde wonderfully handles attributes. This also takes some code and ideas from serde itself.

Some tests are directly adapted from rustc's tests.

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.

rust-derivative's People

Contributors

azriel91 avatar benesch avatar bluss avatar dtolnay avatar dwijnand avatar glandium avatar hcpl avatar kankri avatar mcarton avatar pratyush avatar seeker14491 avatar stanislav-tkach avatar texitoi avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

rust-derivative's Issues

Failed to derive Clone for structs with fields which have references

Describe the bug
The following code will fail to derive.

To Reproduce

use derivative::*;

#[derive(Derivative, Debug)]
#[derivative(Clone(bound = ""), Copy(bound = ""))]
pub struct T<'a, A> {
    a: &'a String,
    b: &'a A,
}

Expected behavior
derive succeeds.

Errors

error[E0308]: mismatched types
 --> src/main.rs:3:10
  |
3 | #[derive(Derivative, Debug)]
  |          ^^^^^^^^^^
  |          |
  |          expected `&String`, found struct `String`
  |          help: consider borrowing here: `&Derivative`

Version (please complete the following information):

  • rustup 1.23.1
  • cargo 1.49.0
  • rustc 1.49.0
  • derivative 2.2.0

Support `const fn new`

const traits aren't stable yet, but we could make the new function we optionally generate with derivative(Debug) const.

We wouldn't be able to use Default::default() for each field though, so it might not work for that many things.

Improved `Partial{Eq,Ord}` derive

Provide derivative({PartialEq, Eq, PartialOrd, Ord}) with:

  • Eq:
    • custom bound
  • PartialEq:
    • custom bound
    • ignore on a field
    • compare_with on a field
  • Ord:
    • custom bound
    • ignore on a field
    • compare_with on a field
  • PartialOrd:
    • custom bound
    • ignore on a field
    • compare_with on a field

enum are tricky¹ as there is no stable way to compare discriminants yet. See rust-lang/rfcs#1696.


1: not for Eq.

Error parsing macro input when using `pub(in path)` syntax.

Using #[derive(Derivative)] produces a parsing error when members of a decorated struct use the pub(in path) syntax. For example:

pub mod foo {
    #[derivative(Debug, Hash)]
    #[derive(Derivative)]
    pub struct Bar {
        #[derivative(Debug = "ignore", Hash = "ignore")]
        pub baz: u32;
        pub(in foo) qux: u32; // Not visible outside of `foo`.
    }
}

This will cause an error like this: help: message: failed to parse macro input: .... Using pub or pub(super) seems to work without any errors.

Docs / v1.x

From the docs

This crate is not stable yet and the attributes might change at any time.

But the version is 1.x. Is the crate stable?

EDIT I assume the crate is stable and the doc message is old, just wanted to check.

Allow for custom documentation.

There should be a way to specify custom documentation for a trait implementation.
Something like:

#[derivative(Default(doc="Returns `None`."))]
enum Option<T> {
    Some(T),
    #[derivative(Default)]
    None,
}

Or maybe for convenience:

#[derivative(Default)]
/// :Default: Returns `None`.
enum Option<T> {
    Some(T),
    #[derivative(Default)]
    None,
}

See also rust-lang/rust#36265.

`format_with` misses bounds on the Dummy struct and its instantiation.

Consider the following rust code:

use derivative::Derivative;

trait Foo {}

fn fmt<T>(t: &T, f: &mut std::fmt::Formatter) -> std::fmt::Result {
    write!(f, "foo")
}

#[derive(Debug)]
struct Qux<'a, T: Foo>(&'a T);

#[derive(Derivative)]
#[derivative(Debug)]
struct Bar<'a, T: Foo>(#[derivative(Debug(format_with="fmt"))] Qux<'a, T>);

fn main() {
}

This fails to build with

error[E0277]: the trait bound `T: Foo` is not satisfied
  --> src/main.rs:14:24
   |
14 | struct Bar<'a, T: Foo>(#[derivative(Debug(format_with="fmt"))] Qux<'a, T>);
   |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `T`
   |
   = help: consider adding a `where T: Foo` bound
note: required by `Qux`
  --> src/main.rs:10:1
   |
10 | struct Qux<'a, T: Foo>(&'a T);
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Expanded, the code looks like:

impl <'a, T: Foo> ::std::fmt::Debug for Bar<'a, T> where T: ::std::fmt::Debug
 {
    fn fmt(&self, __f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        match *self {
            Bar(ref __arg_0) => {
                let mut __debug_trait_builder = __f.debug_tuple("Bar");
                let __arg_0 =
                    {
                        struct Dummy<'a, '_derivative,
                                     T>(&'_derivative Qux<'a, T>,
                                        ::std::marker::PhantomData<(T)>) where
                               T: '_derivative;
                        impl <'a, '_derivative, T: Foo> ::std::fmt::Debug for
                         Dummy<'a, '_derivative, T> where T: '_derivative {
                            fn fmt(&self, __f: &mut ::std::fmt::Formatter)
                             -> ::std::fmt::Result {
                                fmt(&self.0, __f)
                            }
                        }
                        Dummy::<'a, T>(__arg_0, ::std::marker::PhantomData)
                    };
                let _ = __debug_trait_builder.field(&__arg_0);
                __debug_trait_builder.finish()
            }
        }
    }
}

With the code expanded, the error becomes:

error[E0277]: the trait bound `T: Foo` is not satisfied
  --> src/main.rs:23:41
   |
23 |                                      T>(&'_derivative Qux<'a, T>,
   |                                         ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `T`
   |
   = help: consider adding a `where T: Foo` bound
note: required by `Qux`
  --> src/main.rs:10:1
   |
10 | struct Qux<'a, T: Foo>(&'a T);
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The code around line 23 is:

struct Dummy<'a, '_derivative,
             T: Foo>(&'_derivative Qux<'a, T>,
                ::std::marker::PhantomData<(T)>) where
       T: '_derivative;

So what is missing here is the original bound for T, because Qux needs it (as per the error message).

But adding that is not enough in this case, because fixing up the expanded code still fails, with a different error:

error[E0107]: wrong number of lifetime arguments: expected 2, found 1
  --> src/main.rs:33:25
   |
33 |                         Dummy::<'a, T>(__arg_0, ::std::marker::PhantomData)
   |                         ^^^^^^^^^^^^^^ expected 2 lifetime arguments

What works, here, is Dummy::<'a, '_, T>(...).

Marker traits derive

All of ExactSizeIterator's methods have default implementations so we could derive a simple implementation similar to:

impl<'a, T> ExactSizeIterator for Iter<'a, T> {}

Similarly FusedIterator and DoubleEndedSearcher are just a marker traits.

Can default values be specified in some shorter way?

In #[derivative(Default(value="123"))] boilerplate is ten times bigger than the value itself.

Maybe it can just be sometihng like #[default("123")]? This way it's only five times bigger.

The short form may be opt-out with cargo features to provide a way of dealing with conflicts with other derives.

clippy complains about clippy::match_single_binding when deriving Debug

Fresh clippy [as of 2020-02-16] is not happy about match code generated when deriving Debug

warning: this match could be written as a `let` statement
 --> examples/let.rs:6:1
  |
6 | #[derivative(Debug)]
  | ^^^^^^^^^^^^^^^^^^^^
  |
  = note: `#[warn(clippy::match_single_binding)]` on by default
  = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#match_single_binding
help: consider using `let` statement
  |
6 | let Derivative = #[derivative(Debug)];
7 | Derivative
  |

To Reproduce
Here is the code that shows the problem

use derivative::Derivative;

#[derive(Default, Derivative)]
#[derivative(Debug)]
pub struct Foo {
    foo: u8,
}

fn main() {
    let foo1 = Foo::default();
    println!("foo = {:?}", foo1);
}

Expected behavior

warning-clean code

Errors
warning: this match could be written as a let statement
--> examples/let.rs:6:1
|
6 | #[derivative(Debug)]
| ^^^^^^^^^^^^^^^^^^^^
|
= note: #[warn(clippy::match_single_binding)] on by default
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#match_single_binding
help: consider using let statement
|
6 | let Derivative = #[derivative(Debug)];
7 | Derivative
|

Version (please complete the following information):

Please include the output of the following commands, and any other version you think is relevant:

rustup 1.21.1 (7832b2ebe 2019-12-20)
cargo 1.43.0-nightly (3c53211c3 2020-02-07)
rustc 1.43.0-nightly (5e7af4669 2020-02-16)
  • Version of derivative: 1.0.3

Additional context
running cargo expand shows what makes clippy unhappy

#![feature(prelude_import)]
#![no_std]
#[prelude_import]
use ::std::prelude::v1::*;
#[macro_use]
extern crate std;
extern crate derivative;
use derivative::Derivative;
#[derivative(Debug)]
pub struct Foo {
    foo: u8,
}
#[automatically_derived]
#[allow(unused_qualifications)]
impl ::core::default::Default for Foo {
    #[inline]
    fn default() -> Foo {
        Foo {
            foo: ::core::default::Default::default(),
        }
    }
}
#[allow(unused_qualifications)]
impl ::std::fmt::Debug for Foo {
    fn fmt(&self, __f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        match *self {
            Foo { foo: ref __arg_0 } => {
                let mut __debug_trait_builder = __f.debug_struct("Foo");
                let _ = __debug_trait_builder.field("foo", &__arg_0);
                __debug_trait_builder.finish()
            }
        }
    }
}
fn main() {
    let foo1 = Foo::default();
    {
        ::std::io::_print(::core::fmt::Arguments::new_v1(
            &["foo = ", "\n"],
            &match (&foo1,) {
                (arg0,) => [::core::fmt::ArgumentV1::new(arg0, ::core::fmt::Debug::fmt)],
            },
        ));
    };
}

The match *self seems to be the source of grief

Weird behaviors of `format_with`

#[macro_use]
extern crate derivative;

#[derive(Derivative)]
#[derivative(Hash, PartialEq)]
pub struct A {
    #[derivative(Hash(hash_with = "std::hash::Hash::hash"), PartialEq(compare_with = "std::cmp::PartialEq::eq"))]
    v: u64
}

The above code can be compiled successfully while the following code can't

#[macro_use]
extern crate derivative;

#[derive(Derivative)]
#[derivative(Debug)]
pub struct A {
    #[derivative(Debug(format_with = "std::fmt::Debug::fmt"))]
    v: u64
}

However, if the struct uses generics,

#[macro_use]
extern crate derivative;

#[derive(Derivative)]
#[derivative(Debug)]
pub struct A<T: std::fmt::Debug> {
    #[derivative(Debug(format_with = "std::fmt::Debug::fmt"))]
    v: T
}

then it can be compiled.

I don't know why the format_with meta of the Debug attribute can be used only when the field type is generic.

The other weird thing is, when using generics, hash_with and compare_with don't need explicit trait binding.

#[macro_use]
extern crate derivative;

#[derive(Derivative)]
#[derivative(Hash, PartialEq)]
pub struct A<T> {
    #[derivative(Hash(hash_with = "std::hash::Hash::hash"), PartialEq(compare_with = "std::cmp::PartialEq::eq"))]
    v: T
}

The above code can be compiled successfully while the following code can't

#[macro_use]
extern crate derivative;

#[derive(Derivative)]
#[derivative(Debug)]
pub struct A<T> {
    #[derivative(Debug(format_with = "std::fmt::Debug::fmt"))]
    v: T
}

Update `syn` and `quote` to latest versions

One of my projects compiles multiple versions of syn and quote due to different crate dependencies using different versions of these crates. To reduce the number of crates I have to build, I'm going around bumping the syn and quote versions to 0.15 and 0.6 (current latest of each).

Hopefully you don't mind taking a PR for this: #26

Enums with Infallible Variants and Derivative Clone Impls make Clippy Overreact on Nightly

NB: Not sure if fixing this issue is the responsibility of derivative or clippy, but there may be a proactive solution that derivative can implement that can future-proof against these kinds of bugs.

Describe the bug
On nightly, if we implement Clone using derivative::Derivative on the following enum

pub enum Enum<T> {
    /// First Choice
    First,

    /// Second Choice
    Second,

    /// Parameter Marker
    #[doc(hidden)]
    __(Infallible, PhantomData<T>),
}

clippy overreacts with

warning: unreachable expression
   |
   | #[derive(derivative::Derivative)]
   |          ^^^^^^^^^^^^^^^^^^^^^^
   |          |
   |          unreachable expression
   |          any code following this expression is unreachable
   |
   = note: `#[warn(unreachable_code)]` on by default
note: this expression has type `Infallible`, which is uninhabited
   |
   | #[derive(derivative::Derivative)]
   |          ^^^^^^^^^^^^^^^^^^^^^^
   = note: this warning originates in the derive macro `derivative::Derivative` (in Nightly builds, run with -Z macro-backtrace for more info)

Implementing Clone using the standard library derive macro doesn't give the error but it has the undesirable property that it automatically adds the T: Clone constraint where derivative does not.

To Reproduce
Here's a rust playground example with the standard library derive and derivative::Derivative.

Expected behavior
I expected no warning since it did not give a warning in the past (and the warning is not helpful either).

Errors
N/A

Version (please complete the following information):

rustup 1.24.3 (2021-05-31)
cargo 1.56.0-nightly (18751dd3f 2021-09-01)
rustc 1.57.0-nightly (fdf65053e 2021-09-07)
derivative 2.2.0

Additional context
You can also just try to silence the warning from clippy by adding #[allow(unreachable_code)] but no matter where you put it on the enum, above the variant, above the pub enum Enum<T> line, or above the #[derive(derivative::Derivative)] line, clippy still complains. You can only silence the warning by putting a global #![allow(unreachable_code)] but this is not safe.

Publish Ord support as 1.0.4 on crates.io

I need to derive Ord and PartialOrd with custom bound, but that's not available on crates.io. Working fine with the git version.

Can it be possible to publish it on crates.io?

Thanks.

Produce more helpful build errors

For the code:

#[derive(Debug, Clone, Derivative)]                                                                                                                                                                                
#[derivative(                                                                                                                                                                                                      
    PartialEq = "feature_allow_slow_enum",                                                                                                                                                                         
    PartialOrd = "feature_allow_slow_enum",                                                                                                                                                                        
    Ord = "feature_allow_slow_enum",                                                                                                                                                                               
    Eq = "feature_allow_slow_enum"                                                                                                                                                                                               
)] 
pub enum Foo {
    // ...
}

the compiler error message is:

error: proc-macro derive panicked                                                                                                                                                                                  
  --> example.rs:23:24                                                                                                                                                                              
   |                                                                                                                                                                                                               
23 | #[derive(Debug, Clone, Derivative)]                                                                                                                                                                           
   |                        ^^^^^^^^^^                                                                                                                                                                             
   |                                                                                                                                                                                                               
   = help: message: unknown attribute  

This error message is not very helpful (compiling with the --verbose flag does not give more immediately exploitable information).
It would be better to have an error message such as Attribute feature_allow_slow_enum unsuported for trait Eq.

Version

rustup 1.19.0 (2af131cf9 2019-09-08)                                                                                                                                                                                                                                                                                                                                                        
rustc 1.37.0 (eae3437df 2019-08-13)                                                                                                                                                                                
cargo 1.37.0 (9edd08916 2019-08-02)  
  • Version of derivative: git 5ae882d

Deref support.

Describe the bug
The Rust Pattern of wrapping one type in another was suggested to me as a solution of working with reference counting.

struct RcA(a-sys::A);

pub struct A(Rc<RcA>);

The Deref trait would remove .0 from all over the place. The point is to implement Drop for RcA such that the destructor for a-sys::A would be called. My target is ash/Vulkan.

A real world example: Much of the boilerplate is hidden inside macros, so only the unique parts are shown.

impl<T> Device<T> {
    pub fn new(
        instance: Instance<T>,
        physical_device: vk::PhysicalDevice,
        create_info: &vk::DeviceCreateInfo,
        user: T,
    ) -> Result<Self> {
        let inner = unsafe { instance.create_device(physical_device, create_info, None) }
            .context(VkCreateDevice {})?;
        let acceleration_structure_fn = AccelerationStructure::new(&**instance, &inner);
        let ray_tracing_fn = RayTracingPipeline::new(&**instance, &inner);
        Ok(Self(Rc::new(RcDevice {
            inner,
            instance,
            acceleration_structure_fn,
            ray_tracing_fn,
            user,
        })))
    }
}

pub struct RcDevice<T> {
    pub inner: VkDevice,
    pub instance: Instance<T>,
    pub acceleration_structure_fn: AccelerationStructure,
    pub ray_tracing_fn: RayTracingPipeline,
    pub user: T,
}

impl<T> Deref for RcDevice<T> {
    type Target = VkDevice;

    fn deref(&self) -> &Self::Target {
        &self.inner
    }
}

impl<T> Drop for RcDevice<T> {
    fn drop(&mut self) {
        unsafe {
            self.inner.destroy_device(None);
        };
    }
}

impl<T> Fence<T> {
    pub fn new(device: Device<T>, create_info: &vk::FenceCreateInfo, user: T) -> Result<Self> {
        Ok(Self(Rc::new(RcFence {
            inner: unsafe { device.create_fence(create_info, None) }.context(VkCreateFence {})?,
            device,
            user,
        })))
    }
}

pub struct RcFence<T> {
    pub inner: vk::Fence,
    pub device: Device<T>,
    pub user: T,
}

impl<T> Drop for RcFence<T> {
    fn drop(&mut self) {
        unsafe {
            self.device.inner.destroy_fence(**self, None);
        };
    }
}

Fence and CommandBuffer https://gitlab.com/cheako/ash-tray-rs/-/blob/c9a1ea4db239fca214f0cea09030096fc0390c3b/src/vk_helper.rs#L598-925 are two extremes and DescriptoSet is a complex example as well.

Expected behavior

    macro_rules! vk_subinner_types {
        ($($t:ident)*) => ($(
            concat_idents::concat_idents! (RcName = Rc, $t {
                impl<T> Deref for RcName<T> {
                    type Target = vk::$t;

                    fn deref(&self) -> &Self::Target {
                        &self.inner
                    }
                }
            });
        )*)
    }

    macro_rules! vk_inner_types {
        ($($t:ident)*) => ($(
            concat_idents::concat_idents! (RcName = Rc, $t {
                impl<T> Deref for $t<T> {
                    type Target = RcName<T>;

                    fn deref(&self) -> &Self::Target {
                        &self.inner
                    }
                }
            });
        )*)
    }

Most of the types(notably Device and Fence from above) are defined with this macro:

macro_rules! vk_tuple_types {
    ($($t:ident)*) => ($(
        concat_idents::concat_idents! (RcName = Rc, $t {
            #[derive(Derivative)]
            #[derivative(Clone(bound=""))]
            pub struct $t<T> (pub Rc<RcName<T>>);
            impl<T> Deref for $t<T> {
                type Target = RcName<T>;

                fn deref(&self) -> &Self::Target {
                    &self.0
                }
            }
        });
    )*)
}

A keen eye would have noticed that Device has a one-of Deref, actually two types like that. It's because "use ash::Device as VkDevice" != "use ash::vk; vk::Device"

Version (please complete the following information):

rustup --version
cargo --version
rustc --version
$ exit
rustup 1.24.3 (ce5817a94 2021-05-31)
info: This is the version for the rustup toolchain manager, not the rustc compiler.
info: The currently active `rustc` version is `rustc 1.56.0-nightly (ad02dc46b 2021-08-26)`
cargo 1.56.0-nightly (e96bdb0c3 2021-08-17)
rustc 1.56.0-nightly (ad02dc46b 2021-08-26)
  • Version of derivative: 2.2.0

Additional context
I also do a lot of testing with CI and it's obviously not easy to run the above --version script. Though I need that information as well, so if you look at https://gitlab.com/cheako/ash-tray-rs/-/pipelines and https://gitlab.com/cheako/hazel-rs/-/pipelines you'll get more examples of version info and resulting errors. Thought I don't believe you'll find errors there, even for the fixed errors because generic is not Clone where I don't think I pushed any.

Also note that I'd love any criticism of this code, it often keeps me up at night wondering if this code is any good... And now I can add wondering if it's too much Golf.

Adding bounds

Neat project. Here's an idea of something that I needed for a project: A way to use derive(Debug) but control which trait bounds are used on the typarms.

Example:

struct EdgeIndex<Ix>(Ix);
// has custom impl
impl<Ix: IndexType> fmt::Debug for EdgeIndex<Ix> { ... }·

unfortunately the bound from the custom impl becomes viral, so I can't do this:

#[derive(Debug)]
struct Node<Ix> {
next: [EdgeIndex<Ix>; 2]
}

The idea would be something like: #[derivable(bounds(Ix: IndexType))] if that's even possible, to customize which bounds are used for Node's Debug implementation.

Detailed bound control is also useful the same way in Clone in particular, but I guess in most derives.

Another idea, just to tack it on, is a variant of derive clone that also implements Clone::clone_from.

Allow derivative for specialized types.

Eg.

struct Foo<T>(T);
impl Default for Foo<u32> {
  //...
}

This would be similar to the bound, but changing the base type that the derive is on to be specialized.

Revisit `feature_allow_slow_enum`

Currently using derivative on enums results in a warning about feature_allow_slow_enum, referring to docs that say it's currently not possible to generate as efficient code as regular derive because the latter uses an internal / unstable discriminant_value intrinsic for a fast path of different enum tags.

However, as far as I can tell, that note in docs is couple of years outdated, since now we have a safe exposed wrapper of that intrinsic under std::mem::discriminant which returns an opaque newtype to an actual discriminant value, but should be perfectly sufficient for comparing two discriminants with each other in a generated code.

`#[derivative(Debug)]` does not work well with packed structs

Example:

#[derive(Copy, Clone, derivative::Derivative)]
#[derivative(Debug)]
#[repr(C, packed)]
struct Test {
    a: u8,
    b: u32
}
warning: borrow of packed field is unsafe and requires unsafe function or block (error E0133)
 --> src/lib.rs:1:23
  |
1 | #[derive(Copy, Clone, derivative::Derivative)]
  |                       ^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: `#[warn(safe_packed_borrows)]` on by default
  = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
  = note: for more information, see issue #46043 <https://github.com/rust-lang/rust/issues/46043>
  = note: fields of packed structs might be misaligned: dereferencing a misaligned pointer or even just creating a misaligned reference is undefined behavior
  = note: this warning originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)

Default #[derive(Debug)] requires that the struct also derives Copy and Clone:

warning: `#[derive]` can't be used on a `#[repr(packed)]` struct that does not derive Copy (error E0133)
 --> src/main.rs:1:10
  |
1 | #[derive(Debug)]
  |          ^^^^^
  |
  = note: `#[warn(safe_packed_borrows)]` on by default
  = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
  = note: for more information, see issue #46043 <https://github.com/rust-lang/rust/issues/46043>
  = note: this warning originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)

After adding #[derive(Copy, Clone)], default Debug expands to the following (notice the copying of the fields):

impl ::core::fmt::Debug for Test {
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match *self {
            Test {
                a: __self_0_0,
                b: __self_0_1,
            } => {
                let mut debug_trait_builder = f.debug_struct("Test");
                let _ = debug_trait_builder.field("a", &&(__self_0_0));
                let _ = debug_trait_builder.field("b", &&(__self_0_1));
                debug_trait_builder.finish()
            }
        }
    }
}

#[derivative(Debug)] just always references the fields instead, and referencing fields in a packed struct is -- as far as I can tell -- bad:

impl ::std::fmt::Debug for Test {
    fn fmt(&self, __f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        match *self {
            Test {
                a: ref __arg_0,
                b: ref __arg_1,
            } => {
                let mut __debug_trait_builder = __f.debug_struct("Test");
                let _ = __debug_trait_builder.field("a", &__arg_0);
                let _ = __debug_trait_builder.field("b", &__arg_1);
                __debug_trait_builder.finish()
            }
        }
    }
}

Attribute `bound` does not work with `stringify!`

Describe the bug

Using macros, including stringify! as arguments to the bound= errors.

To Reproduce

Compile

#[derivative(PartialEq(bound=stringify!()))]
struct Foo();

Errors

error: invalid attribute: expected literal

Version (please complete the following information):

rustup --version
rustup 1.24.3 (ce5817a94 2021-05-31)
cargo --version
cargo 1.56.0-nightly (cc17afbb0 2021-08-02)
rustc --version
rustc 1.56.0-nightly (ae90dcf02 2021-08-09)
  • Version of derivative: 2.2.0

Please add a way to automate bounds

Custom bounds is powerful, but in many cases, the use case for derivative is to work around rust-lang/rust#26925, and in many of those cases, simple iterative bounds are enough. That is, for:

struct Foo<T, U>(Bar<T>, U);

one will usually just want where Bar<T>: Trait, U: Trait instead of the T: Trait, U: Trait that #[derive] does.

But manually writing that can be tedious and error prone, so a special option to auto-generate those would be nice.

Deriving PartialEq for enum variants

I am wondering is there a way to derive PartialEq<VariantType> for Enum ?
This could be useful for writing protocols, or some other stuff where each variant of enum is strictly unique.

Example of usage:

#[derive(Derivative)]
#[derivative(PartialEq="compare_variants)]
enum HeterogeneousValue {
String(String)
U64(u64)
}

will derive:
impl PartialEq<String> for HeterogeneousValue;
impl PartialEq<U64> for HeterogeneousValue;

The `#derive(Derivative)` attribute does not work in macro afer updating to the latest nightly.

After updating to the latest nightly, #derive(Derivative) does not work in macro anymore.

#[macro_use]
extern crate derivative;

macro_rules! empty_struct {
    (
        $(#[$attr:meta])* $name:ident
    ) => {
        $(#[$attr])* 
        struct $name;
    };
}

empty_struct!(
    #[derive(Derivative)]
    #[derivative(Debug)]
    MyStruct
);

Compiler error:

  --> src/main.rs:14:14
   |
8  |           $(#[$attr])* 
   |  ___________-
9  | |         struct $name;
   | |_____________________- this function has a `self` parameter, but a macro invocation can only access identifiers it receives from parameters
...
14 |       #[derive(Derivative)]
   |                ^^^^^^^^^^ `self` value is a keyword only available in methods with a `self` parameter
   |
   = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)

Version info:

$ rustup --version
rustup 1.22.1 (b01adbbc3 2020-07-08)
$ cargo --version
cargo 1.47.0-nightly (51b66125b 2020-08-19)
$ rustc --version
rustc 1.48.0-nightly (d006f5734 2020-08-28)

Version of derivative: 2.1.1

Improve `Debug` derive with `[u8]`/`str`

This:

#[derive(Debug)]
struct Foo {
    data: Vec<u8>,
}

will always print all vector in a very noisy form. Some filters could be nice:

  • Print shorter version of the data (remove end/remove middle part): Foo { data: [12, 102, 111, 111, 12, …] } or Foo { data: [12, 102, 111, …, 98, 97, 122] }.
  • Print as ascii/UTF-8 when possible: Foo { data: [12, "foo", 12, "bar", 12, "baz"] }.

Invalid attribute error when combined with thiserror

Describe the bug
Combining the derive macros derivative::Derivative and thiserror::Error seems to break arbitrary debug expressions in thiserror.

To Reproduce

use {derivative::Derivative, std::fmt::Debug, thiserror::Error as ThisError};

trait NonDebugTrait {
    type DebugAssociatedType: Debug;
}

#[derive(ThisError, Derivative)]
#[derivative(Debug(bound = "T: NonDebugTrait"))]
enum ThatError<T: NonDebugTrait> {
    #[error("error {:?}", .0)]
    Variant(T::DebugAssociatedType),
}

fn main() {
    println!("Hello, world!");
}

Expected behavior
Successful compilation.

Errors

error: invalid attribute: expected identifier or literal
  --> src/main.rs:10:27
   |
10 |     #[error("error {:?}", .0)]
   |                           ^

Version (please complete the following information):

rustup --version # n/a
cargo --version # cargo 1.49.0-nightly (2af662e22 2020-11-12)
rustc --version # rustc 1.50.0-nightly (603ab5bd6 2020-11-15)
  • Version of derivative: 2.1.1

Additional context
I originally reported this at dtolnay/thiserror#111, but @dtolnay indicated this was a problem with rust-derivative, not thiserror.

Using `derive(Derivative)` with no further attribute should be an error

Eg.

#[derive(derivative::Derivative)]
struct Error;

should not compile.
Less contrived example:

#[derive(derivative::Derivative)]
#[derive(Clone)] // user probably meant to use `#[derivative(Clone)]` here
struct Error;

For instance, the following:

#[derive]
struct Error;

generates

error: malformed `derive` attribute input
 --> src/lib.rs:1:1
  |
1 | #[derive]
  | ^^^^^^^^^ help: missing traits to be derived: `#[derive(Trait1, Trait2, ...)]`

Enumeration derives

On enumerations with only simple variants, I've often wanted to have some functions to:

  • get all variants (make const array? some kind of iterator?)
  • given a variant, get the next variant (should this return an Option for end-cases or go round?)
  • get a human readable name for a variant
    • by default should just be based on the case of the variant, eg. PermissionDenied would give “permission denied”
    • have a human="some value" attribute on variants to change that
  • {enum}{integer} conversion

Enum: Unknown attribute "format_with" for trait Debug

Trying to use format_with for Debug within an enum, leads into this error.

grafik

use derivative::*;
#[derive(Derivative)]
#[derivative(Debug)]
#[repr(u8)]
enum Enum {
    #[derivative(Debug(format_with = "hex_fmt"))]
    A,
}

Is there a chance to fix this?

Add an Option to use a Format String to Structs

My Idea is to use something similar to thiserror where you specify a format string that get's filled with the fields:

#[derive(Error, Debug)]
pub enum DocumentError {
    #[error("Invalid {0}: `{1}`")]
    InvalidAttribute(String, String),
}

This could look like this for Debug of structs:

#[derive(Derivative)]
#[derivative(Debug(format="({x},{y}: {w})"))]
pub struct Point {
    pub x: f32,
    pub y: f32,
    pub w: f32,
}

I think most of the Time this is enough for formatting, and you don't actually need a function for that.

Allow skipping fields in Clone.

It would be nice to be able to skip fields during clone. For example caches and other fields can be safely ignored as they won't change the semantics of the object.

Of course a default value will be required. This should probably be Default::default() by default but customizing it would be useful (like you can do for derivative(Default)).

Hash doesn't work for generic enums

Describe the bug

The code generated by deriving Hash for generic enumerations leads to issues with type inference.

To Reproduce

#[derive(derivative::Derivative)]
#[derivative(Hash)]
enum Enumeration<T> {
    Variant(T),
}

Expected behavior

I expect valid code akin to the example below to be generated

impl<T: Hash> Hash for Enumeration<T> {
    fn hash<H: Hasher>(&self, state: &mut H) {
        use Enumeration::*;
        match self {
            Variant(t) => t.hash(state),
        }

        discriminant(self).hash(state);
    }
}

Errors

error[E0282]: type annotations needed
 --> src/test.rs:3:6
  |
3 |   enum Enumeration<T> {
  |  ______^
4 | |     Variant(T),
  | |___________^ cannot infer type for type parameter `T` declared on the enum `Enumeration`

Version (please complete the following information):

rustup --version
rustup 1.21.1 (2020-02-23)

cargo --version
cargo 1.43.0-nightly (bda50510d 2020-03-02)

rustc --version
rustc 1.43.0-nightly (c20d7eecb 2020-03-11)
  • Version of derivative: 2.0.2

Additional context

N/A

Support no_std

Hi!

Is there any reason for not supporting no_std? This is a proc macro crate, so it should not really matter whether the generated code depends on std or not, should it?

Skip enum fields for `Debug` implementation

One common pattern in rust is to represent commands with enums. Multiple related commands map to different enum members and their parameters map to fields. Associated input/output buffers or other data structures may be included as enum fields. These often require a custom Debug implementation to display the control parameters but omit large buffers or non-debug data structures. This would be a nice pattern to support.

Examples with proposed syntax options

Enums with named fields

enum Command {
    #[derivative(Debug(skip("buffer"))]
    Rebuild { target: IsDebug, buffer: NotDebug },
}

Enums with unnamed fields

// Explicit by field
enum Command {
    Rebuild(IsDebug, #[derivative(Debug="skip")] NotDebug),
}

// Explicit by type
enum Command {
    #[derivative(Debug(skip(NotDebug)))]
    Rebuild(IsDebug, NotDebug),
}

// Implicit
enum Command {
    #[derivative(Debug="skip_non_debug")]
    Rebuild(IsDebug, NotDebug),
}

Generated code

impl Debug for Command {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Command::Rebuild(val, _) => f.debug_tuple("Rebuild").field(&val).finish(),
        }
    }
}

clippy warns when using clone_from = "true"

Describe the bug

clippy warns when using clone_from = "true"

To Reproduce

use derivative::Derivative;

#[derive(Derivative)]
#[derivative(Clone(clone_from = "true"))]
pub struct Foo {}

fn main() {}
cargo clippy 
    Checking test-rs v0.1.0 (/home/xxx/dev/test-rs)
warning: unneeded `return` statement
 --> src/main.rs:3:10
  |
3 | #[derive(Derivative)]
  |          ^^^^^^^^^^
  |
  = note: `#[warn(clippy::needless_return)]` on by default
  = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_return
  = note: this warning originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)

warning: 1 warning emitted

    Finished dev [unoptimized + debuginfo] target(s) in 0.14s

Expected behavior

No warning

Version (please complete the following information):

Please include the output of the following commands, and any other version you think is relevant:

rustup 1.21.1 (7832b2ebe 2019-12-20)
cargo 1.44.1 (88ba85757 2020-06-11)
rustc 1.44.1 (c7087fe00 2020-06-17)
  • Version of derivative:
name = "derivative"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb582b60359da160a9477ee80f15c8d784c477e69c217ef2cdd4169c24ea380f"

"error: cannot find value `__f` in this scope" within macro body

Describe the bug
The following snippet fails to compile:

use derivative::Derivative;

macro_rules! gen {
    ($name:ident) => {
        #[derive(derivative::Derivative)]
        #[derivative(Debug)]
        pub struct $name {
            a: i32
        }
    };
}

gen!(Test);

fn main() {
    println!("Hello, world!");
}

See error below.

To Reproduce
See snippet above.

Expected behavior
The snippet should compile and derivative should generate a debug impl for every invocation of gen.

Errors

error[E0425]: cannot find value `__f` in this scope
 --> src\main.rs:5:18
  |
5 |         #[derive(derivative::Derivative)]
  |                  ^ not found in this scope

Version (please complete the following information):

rustup 1.19.0 (2af131cf9 2019-09-08)
cargo 1.39.0-nightly (3596cb86b 2019-09-19)
rustc 1.39.0-nightly (97e58c0d3 2019-09-20)
derivative 1.0.3

Additional context
The error seems to appear only when the struct name is a macro parameter.

Reduce `maxAge` for some README badges to freshen them more frequently

The crates.io badge in README.md shows 1.0.1 when 1.0.2 is already out:

(link that generated this badge: https://img.shields.io/crates/v/derivative.svg?maxAge=2592000)

Notice that maxAge there is 2592000 seconds which equals 30 days. This is overkill and I think having maxAge=3600 (1 hour) is enough --- the Travis and Appveyor badges already use 3600 too.

EDIT: for anyone wondering why I wrote "shows 1.0.1 when 1.0.2 is already out", that's because at the time of writing it did show "v1.0.1" instead of whatever version that is the latest.

`#[derivative(Debug)]` triggers `clippy::use_self` pedantic lint

Describe the bug

error: unnecessary structure name repetition
 --> examples/derivative_bug.rs:7:12
  |
7 | pub struct Foo {}
  |            ^^^ help: use the applicable keyword: `Self`
  |
note: lint level defined here
 --> examples/derivative_bug.rs:1:9
  |
1 | #![deny(clippy::pedantic)]
  |         ^^^^^^^^^^^^^^^^
  = note: #[deny(clippy::use_self)] implied by #[deny(clippy::pedantic)]
  = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#use_self

To Reproduce

#![deny(clippy::pedantic)]

use derivative::Derivative;

#[derive(Derivative)]
#[derivative(Debug)]
pub struct Foo {}

fn main() {}

Version (please complete the following information):

aleksi@Aleksis-MacBook-Pro natt % rustup --version
rustup 1.18.3 (435397f48 2019-05-22)
aleksi@Aleksis-MacBook-Pro natt % cargo --version
cargo 1.37.0 (9edd08916 2019-08-02)
aleksi@Aleksis-MacBook-Pro natt % rustc --version
rustc 1.37.0 (eae3437df 2019-08-13)
  • Version of derivative: 1.0.3

Feature request: Add a way to display only part of a collection-like type

I want to indicate in the debug representation that a field contains Vec or slice and give a preview of what sort of data it contains without showing the whole slice. I propose an option like so

#[derivative(Debug=take(10))]
field: Vec<u8>

which translates to whatever the debug formatting of the following is

field.iter().take(10).collect::Vec<_>()

I'm happy to make a PR if this is something you're open to.

PartialEq + "compare with" for two fields?

Describe the bug
I have type like this:

struct Foo {
  default_idx: Option<usize>,
  list: Vec<Boo>,
  //many other fields
}

I want generate PartialEq in such way that:

    fn eq(&self, o: &Foo) -> bool {
         self.default_idx.map(|idx| &self.list[idx]) == o.default_idx.map(|idx| &o.list[idx]) &&      
      // && self.other_fields == o.other_fields

It would be nice if there is way to generate code for comparing two fields at once.
The most simple way as I see will be syntax like this:

#[derivative(PartialEq(fn = compare_foo))]
struct Foo {
 #[derivative(PartialEq="ignore")]
default_idx: Option<usize>,
  list: Vec<Boo>,
 //many other fields
}

and it generate compare_foo function that compare only many other fields:

fn compare_foo(lhs: &Foo, rhs: &Foo) -> bool {
 self.other_fields == o.other_fields
}

so I can write PartialEq by hands and compare several special fields in the way I want,
and then use generated compare_foo for other fields.

  • Version of derivative: 1.0.3

`std::error::Error` derive

Since std::error::Error::description returns a &str, it's quite common to return a default static string here. Also it's common to leave cause return None.

We could have an attribute like this:

#[derive(Derivative)]
#[derivative(Error(description="some description"))]
struct Foo

Derive Sync and Send

Send and sync are implemented by default only when all generic arguments are send/sync, which is not always necessary. To get them without bounds one needs to write

unsafe impl<T> Send for SomeType<T> {}
unsafe impl<T> Sync for SomeType<T> {}

for every such type. It would be more convenient to have #[derivative(Sync(bounds=""), Send(bounds=""))].

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.