Skip to content

Conversation

balsoft
Copy link

@balsoft balsoft commented Aug 26, 2025

Resolves #149

Synopsis

When deriving From, it is often useful to let some fields be a certain
"constant" default value instead of being part of the source tuple type.

The most obvious such value is Default::default(), but specifying
other values is very useful as well.

This is my first time touching proc-macros, please handle with care :)

The tests pass though (except for one that was broken before my changes), and this change shouldn't break anything.

I'm also open to alternative designs (e.g. #[from(default = <...>)] instead of #[from(...)])

Solution

Add handling of #[from] and #[from(<default value>)] attrs
on struct (and enum struct) fields. For more information about the
handling details, please consult the included documentation changes
and/or tests.

Excerpt from documentation

If you add a #[from(<default value>)] attribute to any fields of the struct,
then those fields will be omitted from the tuple and be set to the default value
in the implementation:

# use std::collections::HashMap;
#
# use derive_more::From;
#
#[derive(Debug, From, PartialEq)]
struct MyWrapper {
    inner: u8,
    #[from(1)]
    not_important: u32,
    #[from(HashMap::new())]
    extra_properties: HashMap<String, String>,
}

assert_eq!(MyWrapper { inner: 123, not_important: 1, extra_properties: HashMap::new(), }, 123.into());

If you add a #[from] value to any fields of the struct, then only those
fields will be present in the tuple and the rest will be either set to
Default::default() or taken from their default values specified in
#[from(<default value>)]:

# use std::collections::HashMap;
#
# use derive_more::From;
#
#[derive(Debug, From, PartialEq)]
struct Location {
    #[from]
    lat: f32,
    #[from]
    lon: f32,
    #[from(String::from("Check out my location!"))]
    description: String,
    extra_properties: HashMap<String, String>,
}

// This is equivalent to:

// #[derive(Debug, From, PartialEq)]
// struct Location {
//     lat: f32,
//     lon: f32,
//     #[from(String::from("Check out my location!"))]
//     description: String,
//     #[from(Default::default())]
//     extra_properties: HashMap<String, String>,
// }


assert_eq!(
    Location {
        lat: 41.7310,
        lon: 44.8067,
        description: String::from("Check out my location!"),
        extra_properties: Default::default(),
    },
    (41.7310, 44.8067).into()
);

Checklist

  • Documentation is updated (if required)
  • Tests are added/updated (if required)
  • CHANGELOG entry is added (if required)

@balsoft balsoft force-pushed the custom-from-values branch 2 times, most recently from fa62dc7 to cd72645 Compare August 26, 2025 10:03
When deriving `From`, it is often useful to let some fields be a certain
"constant" default value instead of being part of the source tuple type.

The most obvious such value is `Default::default()`, but specifying
other values is very useful as well.

As such, add handling of `#[from]` and `#[from(<default value>)]` attrs
on struct (and enum struct) fields. For more information about the
handling details, please consult the included documentation changes
and/or tests.

Closes JelteF#149
My changes have broken a test, which actually was actually incorrect
twice: it wasn't doing the right thing (forwarding the From impl to
the field type), but it wasn't testing the right thing either so it
succeeded.

Make the test actually check that from forwarding works, and make the
forwarding actually work.
@balsoft balsoft force-pushed the custom-from-values branch from cd72645 to ffa1cb3 Compare August 26, 2025 10:08
@balsoft balsoft changed the title Allow custom #[from] attrs on fields #[derive(From)]: allow specifying default values for struct/enum variant fields Aug 26, 2025
Copy link
Owner

@JelteF JelteF left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the work on this. Some initial thoughts

inner: u8,
#[from(1)]
not_important: u32,
#[from(HashMap::new())]
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I'd prefer to have the syntax be:

Suggested change
#[from(HashMap::new())]
#[from(value(HashMap::new()))]

or

Suggested change
#[from(HashMap::new())]
#[from(default(HashMap::new()))]

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm; I think this makes it a bit more annoying to handle, since we then have to parse the token tree (e.g. value(HashMap::new()) ourselves, and I couldn't find an easy way to do that with syn; the most obvious syn::ExprCall doesn't have a Parse trait. Do you have any pointers?

```


If you add a `#[from]` value to any fields of the struct, then only those
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd also want a way to specify the opposite:

#[from(default)]
extra_properties: HashMap<String, String>,

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this can be handled with #[from(Default::default())] (at least in the current design).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Allow #[from] or #[from(ignore)] in regular structs
2 participants