Serde: Support default literals

Created on 10 Jun 2016  Â·  16Comments  Â·  Source: serde-rs/serde

#[serde(default="0.1")]
pub n: f32

There is an ambiguity when the field is of function type so we need to figure that out.

derive enhancement

Most helpful comment

Seems like an easy route would be #[serde(default_value = "0.1")]. It's a bit more wordy but would allow us to get the functionality out there, and we can always deprecate default_value if a better approach comes around.

All 16 comments

This needs to be fleshed out a lot more before it can be implemented so please chime in if you have ideas for how this should work.

I am glad we implemented #[serde(default="...")] and it has been widely useful but backward compatibility is going to be tricky. To start brainstorming, here are a few possible alternatives:

  • Another level of nesting

    • #[serde(default(expr="0.1"))] - we insert the expression verbatim into the generated code

    • This is flexible but clunky.

  • Look for parentheses to denote an expression

    • #[serde(default="(0.1)")]

    • This is hacky and doesn't provide a path forward for deprecating the current syntax.

  • Use a different attribute name

    • #[serde(missing="0.1")], #[serde(default_expr="0.1")]

    • But default is such a perfect fit.

  • Treat as an expression if it fails to parse as a path

    • #[serde(default="0.1")]

    • This is ambiguous in many cases, for example enums: #[serde(default="Color::Yellow")]

Seems like an easy route would be #[serde(default_value = "0.1")]. It's a bit more wordy but would allow us to get the functionality out there, and we can always deprecate default_value if a better approach comes around.

Would it be possible to support closures? Something like:

#[serde(default="||{ 0.1 }")]
pub n: f32

The closure route would be the most logical for me (and the first thing I tried when exploring how to do what I wanted). However, I think default_expr would be by far the clearest.

I would have preferred default=3 to be the literal mode and default_func the function-based approach, but eh, can't do much about that now.

As reported in #1031, unit variants would be nice to support as well. Rust no longer enforces that the value in an attribute is a literal.

Any progress on that? Right now I am forced to write functions like
fn one() -> u32 { 1 }
and it really looks silly

I agree with @sfackler and think #[serde(default_value = "0.1")] is a way to go

Surely #[serde(default_value = 0.1)] is better - and then any expression can be provided?

In fact, that can replace any of the current usage right?

@dtolnay Would a PR be accepted for this? Using proc macros, it shouldn't be terribly difficult to generate a function (which can have an #[inline] hint) and do something along the lines of #[serde(default = #fn_name)] in the expansion.

I can certainly work on this if it's a feature you and the other maintainers are interested in.

Just being bitten by this, currently writing a lot of
fn one() -> u32 { 1 }
which is very clunky.

I don't know how to drive this forward, but I think a very small change to the compiler (or some other trick I have yet to find) could enable the existing attribute to work with values too: https://internals.rust-lang.org/t/implementing-traits-for-function-types/5888
(the playground code linked in that thread uses specialization but IIRC there was a way around that)

@dtolnay Would a PR be accepted that resolves this? I asked a few weeks ago and received no answer. I don't think there's any question people want this.

This crate could also give inspiration: https://github.com/idanarye/rust-smart-default

It allows specifying literals as well as code for the default value.

How about using closure notation? Eg just like you’d unwrap_or_else(some_void_func) or unwrap_or_else(||1.0f64)

This would be backwards compatible, consistent with existing Rust syntax, and even similar to the syntax you’d use for providing a “default” value to an option-wrapped-type.

I’d also consider dropping the quotes, that has always confused me, but I don’t know if there’s some parse thing that makes them necessary.

Considering how closures are generally used, it’s expected that the compiler will be good at optimizing them so you might actually be able to drop it in as an actual closure (so it could capture variables from the enclosing scope)

There is now a PR open: #1490

I'd love to help get this feature completed; though I don't have much experience developing proc macros I've started studying the relevant parts of serde_derive and it doesn't seem too bad.

Regarding the concerns made in https://github.com/serde-rs/serde/pull/1490#pullrequestreview-288464564, while I agree that it may be nicer to be able to use paths and expressions, that shouldn't be a blocker for this particular feature. A MVP just with support for literals would still be incredibly useful.

Was this page helpful?
0 / 5 - 0 ratings