Repository: clap-rs/clap (rust)

Issue:
use `default_value_t` with `ArgEnum` without manually implementing `Display`
### Please complete the following tasks

- [X] I have searched the [discussions](https://github.com/clap-rs/clap/discussions)
- [X] I have searched the existing issues

### Clap Version

master

### Describe your use case

deriving argenums with default values is currently a little clumsy, since you have to manually implement `Display`, and you need to manually ensure that the `Display` implementation is aligned with the derived `ArgEnum` implementation


for example

```rust
#[derive(Debug, Parser)]
pub struct List {

    #[clap(long, arg_enum, default_value_t]
    variant: Variant
}

#[derive(Debug, Clone, Copy, ArgEnum)]
enum Variant {
    ObjectA,
    ObjectB
}

impl std::fmt::Display for Variant {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let s = match self {
            Self::ObjectA => "object-a", // or is it "object_a"?
            Self::ObjectB => "object-b",
        };
        write!(f, "{}", s)
    }
}
```

what i'd *like* is to omit the manual `Display` implementation

### Describe the solution you'd like

`#[derive(ArgEnum)]` should generate a `Display` implementation which is consistent with the input values that it accepts

### Alternatives, if applicable

- `clap` provide a `Display` derive that is implemented in terms of `ArgEnum`
- Specialize `default_value_t` support for `ArgEnum`

### Additional Context

one possible edge case is that the derived `Display` implementation could conflict with another `Display` implementation (in which case `default_value_t` shouldn't be used anyway!)

therefore the generated `Display` implementation could have a flag to opt in or out (i would vote for making it opt out)

Pull request:
feat(derive): Don't require Display for default ArgEnum
While I'm unsure how much type specialization we should do, we
intentionally have the `arg_enum` attribute for doing special behavior
based on it, so let's take advantage of it.

By loosening `PossibleValue`s lifetimes for this, we also avoid a `lazy_static!` and string allocation.

Fixes #3185

Patch:
diff --git a/clap_derive/src/attrs.rs b/clap_derive/src/attrs.rs
index 648be95b..b59aa2ee 100644
--- a/clap_derive/src/attrs.rs
+++ b/clap_derive/src/attrs.rs
@@ -399,7 +399,9 @@ impl Attrs {
     fn push_attrs(&mut self, attrs: &[Attribute]) {
         use ClapAttr::*;
 
-        for attr in parse_clap_attributes(attrs) {
+        let parsed = parse_clap_attributes(attrs);
+        for attr in &parsed {
+            let attr = attr.clone();
             match attr {
                 Short(ident) => {
                     self.push_method(ident, self.name.clone().translate_char(*self.casing));
@@ -463,16 +465,25 @@ impl Attrs {
                         quote!(<#ty as ::std::default::Default>::default())
                     };
 
-                    let val = quote_spanned!(ident.span()=> {
-                        clap::lazy_static::lazy_static! {
-                            static ref DEFAULT_VALUE: &'static str = {
+                    let val = if parsed.iter().any(|a| matches!(a, ArgEnum(_))) {
+                        quote_spanned!(ident.span()=> {
+                            {
                                 let val: #ty = #val;
-                                let s = ::std::string::ToString::to_string(&val);
-                                ::std::boxed::Box::leak(s.into_boxed_str())
-                            };
-                        }
-                        *DEFAULT_VALUE
-                    });
+                                val.to_possible_value().unwrap().get_name()
+                            }
+                        })
+                    } else {
+                        quote_spanned!(ident.span()=> {
+                            clap::lazy_static::lazy_static! {
+                                static ref DEFAULT_VALUE: &'static str = {
+                                    let val: #ty = #val;
+                                    let s = ::std::string::ToString::to_string(&val);
+                                    ::std::boxed::Box::leak(s.into_boxed_str())
+                                };
+                            }
+                            *DEFAULT_VALUE
+                        })
+                    };
 
                     let raw_ident = Ident::new("default_value", ident.span());
                     self.methods.push(Method::new(raw_ident, val));
diff --git a/clap_derive/src/derives/arg_enum.rs b/clap_derive/src/derives/arg_enum.rs
index 045a0af7..f128a6f3 100644
--- a/clap_derive/src/derives/arg_enum.rs
+++ b/clap_derive/src/derives/arg_enum.rs
@@ -51,7 +51,7 @@ pub fn gen_for_enum(name: &Ident, attrs: &[Attribute], e: &DataEnum) -> TokenStr
     let to_possible_value = gen_to_possible_value(&lits);
 
     quote! {
-        #[allow(dead_code, unreachable_code, unused_variables)]
+        #[allow(dead_code, unreachable_code, unused_variables, unused_braces)]
         #[allow(
             clippy::style,
             clippy::complexity,
diff --git a/clap_derive/src/derives/args.rs b/clap_derive/src/derives/args.rs
index 461892a1..2db658eb 100644
--- a/clap_derive/src/derives/args.rs
+++ b/clap_derive/src/derives/args.rs
@@ -73,7 +73,7 @@ pub fn gen_for_struct(
     quote! {
         #from_arg_matches
 
-        #[allow(dead_code, unreachable_code, unused_variables)]
+        #[allow(dead_code, unreachable_code, unused_variables, unused_braces)]
         #[allow(
             clippy::style,
             clippy::complexity,
diff --git a/clap_derive/src/parse.rs b/clap_derive/src/parse.rs
index 504701fc..64d80a77 100644
--- a/clap_derive/src/parse.rs
+++ b/clap_derive/src/parse.rs
@@ -21,6 +21,7 @@ pub fn parse_clap_attributes(all_attrs: &[Attribute]) -> Vec<ClapAttr> {
 }
 
 #[allow(clippy::large_enum_variant)]
+#[derive(Clone)]
 pub enum ClapAttr {
     // single-identifier attributes
     Short(Ident),
diff --git a/examples/derive_ref/README.md b/examples/derive_ref/README.md
index 4acebb90..2c341658 100644
--- a/examples/derive_ref/README.md
+++ b/examples/derive_ref/README.md
@@ -174,7 +174,7 @@ In addition to the raw attributes, the following magic attributes are supported:
   - Without `<expr>`: fills the field with `Default::default()`
 - `default_value = <str>`: `clap::Arg::default_value` and `clap::Arg::required(false)`
 - `default_value_t [= <expr>]`: `clap::Arg::default_value` and `clap::Arg::required(false)`
-  - Requires `std::fmt::Display`
+  - Requires `std::fmt::Display` or `#[clap(arg_enum)]`
   - Without `<expr>`, relies on `Default::default()`
 
 ### Arg Types
diff --git a/src/build/arg/possible_value.rs b/src/build/arg/possible_value.rs
index 142c0530..f9433315 100644
--- a/src/build/arg/possible_value.rs
+++ b/src/build/arg/possible_value.rs
@@ -134,13 +134,13 @@ impl<'help> PossibleValue<'help> {
 impl<'help> PossibleValue<'help> {
     /// Get the name of the argument value
     #[inline]
-    pub fn get_name(&self) -> &str {
+    pub fn get_name(&self) -> &'help str {
         self.name
     }
 
     /// Get the help specified for this argument, if any
     #[inline]
-    pub fn get_help(&self) -> Option<&str> {
+    pub fn get_help(&self) -> Option<&'help str> {
         self.help
     }
 
@@ -151,7 +151,7 @@ impl<'help> PossibleValue<'help> {
     }
 
     /// Get the name if argument value is not hidden, `None` otherwise
-    pub fn get_visible_name(&self) -> Option<&str> {
+    pub fn get_visible_name(&self) -> Option<&'help str> {
         if self.hide {
             None
         } else {
@@ -162,7 +162,7 @@ impl<'help> PossibleValue<'help> {
     /// Returns all valid values of the argument value.
     ///
     /// Namely the name and all aliases.
-    pub fn get_name_and_aliases(&self) -> impl Iterator<Item = &str> {
+    pub fn get_name_and_aliases(&self) -> impl Iterator<Item = &'help str> + '_ {
         iter::once(&self.name).chain(&self.aliases).copied()
     }
 


Test patch:
diff --git a/tests/derive/arg_enum.rs b/tests/derive/arg_enum.rs
index 6f439919..22f86c21 100644
--- a/tests/derive/arg_enum.rs
+++ b/tests/derive/arg_enum.rs
@@ -52,12 +52,6 @@ fn default_value() {
         }
     }
 
-    impl std::fmt::Display for ArgChoice {
-        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
-            std::fmt::Display::fmt(self.to_possible_value().unwrap().get_name(), f)
-        }
-    }
-
     #[derive(Parser, PartialEq, Debug)]
     struct Opt {
         #[clap(arg_enum, default_value_t)]