Search code examples
rustcommand-linecommand-line-interfacecommand-line-argumentsclap

Understanding `default_value_t` and `default_missing_value` in clap v4 crate for Rust


I'm a bit confused on the distinction between the default_value_t and default_missing_value attributes in the process of building my first command-line program using the clap crate in Rust. The following code snippet was taken from clap's documentation on Git's cookbook for the derive API as an example:

#[derive(Debug, Subcommand)]
enum Commands {
    /// Compare two commits
    Diff {
        #[arg(value_name = "COMMIT")]
        base: Option<OsString>,
        #[arg(value_name = "COMMIT")]
        head: Option<OsString>,
        #[arg(last = true)]
        path: Option<OsString>,
        #[arg(
            long,
            require_equals = true,
            value_name = "WHEN",
            num_args = 0..=1,
            default_value_t = ColorWhen::Auto, // <- This 
            default_missing_value = "always",  // <- and this part.
            value_enum
        )]
        color: ColorWhen,
    },

#[derive(ValueEnum, Copy, Clone, Debug, PartialEq, Eq)]
enum ColorWhen {
    Always,
    Auto,
    Never,
}

I have pointed it out with comments in the code example.

As I understand it, default_missing_value appears to be useful when, let's say an argument --foo can take multiple values. And in such a case when a value isn't provided, it defaults to whatever default_missing_value is set to. However to me, default_value_t seems to serve the same or a similar purpose?

Could someone help clarify the difference between default_value_t and default_missing_value? Examples are much appreciated.


Solution

  • All the various directives are documented more extensively as methods of clap::Arg.

    As I understand it, default_missing_value appears to be useful when, let's say an argument --foo can take multiple values.

    It does not have anything to do with mutliple values.

    However to me, default_value_t seems to serve the same or a similar purpose?

    They have similar but different purposes, per the documentation linked above:

    pub fn default_value(self, val: impl IntoResettable<OsStr>) -> Arg
    

    Value for the argument when not present.

    pub fn default_missing_value(self, val: impl IntoResettable<OsStr>) -> Arg
    

    Value for the argument when the flag is present but no value is specified.

    So default_value[0] is invoked when the argument is missing from the command line e.g.

    git diff
    

    while default_missing_value is invoked when the argument is present but has no value e.g.

    git diff --color
    

    In the case of git the former means "defer to the configuration file (/ whether stdout is a term)" while the latter means "force colorized output". That is why they are both set, with different behaviours.


    [0] the declarative default_value_t and default_value map to essentially the same thing, it's just that the former is post-parse (e.g. it might be a number) while the latter is pre-parse (it's always a string literal).