【问题标题】:How can I use enums in structopt?如何在 structopt 中使用枚举?
【发布时间】:2019-07-08 07:16:03
【问题描述】:

我想让StructOpt 与枚举一起使用,这样每次用户通过-d sunday 时,它都会被解析为Day::Sunday

#[macro_use]
extern crate structopt;

use std::path::PathBuf;
use structopt::StructOpt;

// My enum
enum Day {
    Sunday, Monday
}

#[derive(Debug, StructOpt)]
#[structopt(name = "example", about = "An example of StructOpt usage.")]
struct Opt {
    /// Set speed
    #[structopt(short = "s", long = "speed", default_value = "42")]
    speed: f64,
    /// Input file
    #[structopt(parse(from_os_str))]
    input: PathBuf,
    /// Day of the week
    #[structopt(short = "d", long = "day", default_value = Day::Monday)]
    day: Day,
}

fn main() {
    let opt = Opt::from_args();
    println!("{:?}", opt);
}

我目前的最佳解决方案是使用Option<String> 作为类型并传递自定义parse_day()

fn parse_day(day: &str) -> Result<Day, ParseError> {
    match day {
        "sunday" => Ok(Day::Sunday),
        _ => Ok(Day::Monday)
    }
    Err("Could not parse a day")
}

【问题讨论】:

    标签: enums rust command-line-arguments structopt


    【解决方案1】:

    Struct-opt 接受任何实现FromStr 的类型,这与您的parse_day 函数相距不远:

    use std::str::FromStr;
    
    // any error type implementing Display is acceptable.
    type ParseError = &'static str;
    
    impl FromStr for Day {
        type Err = ParseError;
        fn from_str(day: &str) -> Result<Self, Self::Err> {
            match day {
                "sunday" => Ok(Day::Sunday),
                "monday" => Ok(Day::Monday),
                _ => Err("Could not parse a day"),
            }
        }
    }
    

    此外,default_value 应该是一个字符串,它将使用from_str 解释为Day

    #[structopt(short = "d", long = "day", default_value = "monday")]
    day: Day,
    

    【讨论】:

    • 至少对于 Rust 1.46,ParseError 看起来并不存在(?)。
    • "ParseError" 从 rust 1.46 起不起作用>,现在我将如何实施这个解决方案?在这种情况下,我可以用什么替换 ParseError?
    【解决方案2】:

    @kennytm's approach 有效,但arg_enum! 宏是一种更简洁的方法,如this example from structopt 所示:

    arg_enum! {
        #[derive(Debug)]
        enum Day {
            Sunday,
            Monday
        }
    }
    
    #[derive(StructOpt, Debug)]
    struct Opt {
        /// Important argument.
        #[structopt(possible_values = &Day::variants(), case_insensitive = true)]
        i: Day,
    }
    
    fn main() {
        let opt = Opt::from_args();
        println!("{:?}", opt);
    }
    

    这将让您将工作日解析为Sundaysunday

    【讨论】:

      【解决方案3】:

      错误信息是:

      error[E0277]: the trait bound `Day: std::str::FromStr` is not satisfied
        --> src/main.rs:22:17
         |
      22 | #[derive(Debug, StructOpt)]
         |                 ^^^^^^^^^ the trait `std::str::FromStr` is not implemented for `Day`
         |
         = note: required by `std::str::FromStr::from_str`
      

      您可以通过为Day 实现FromStr(参见kennytm's answer)来解决此问题,如消息所示,或者通过为Day 定义解析函数:

      fn parse_day(src: &str) -> Result<Day, String> {
          match src {
              "sunday" => Ok(Day::Sunday),
              "monday" => Ok(Day::Monday),
              _ => Err(format!("Invalid day: {}", src))
          }
      }
      

      并使用try_from_str 属性指定它:

      /// Day of the week
      #[structopt(short = "d", long = "day", parse(try_from_str = "parse_day"), default_value = "monday")]
      day: Day,
      

      【讨论】:

      • 最佳答案!只是缺少不区分大小写:)
      • @decades 不区分大小写在问题中没有提到,它只是启用它的一个额外属性。
      • 我这样解决了:match src.to_lowercase().as_ref() {...
      猜你喜欢
      • 2018-11-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-26
      • 2013-02-11
      • 2017-09-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多