【问题标题】:Parse String to most appropriate type automatically自动将字符串解析为最合适的类型
【发布时间】:2020-10-07 15:22:28
【问题描述】:

如何将一些字符串解析为最合适的类型?

我知道有.parse::<>()方法,但是你需要像这样提前指定类型:

fn main() {
    let val = String::from("54");
    assert_eq!(val.parse::<i32>().unwrap(), 54i32);
    
    let val = String::from("3.14159");
    assert_eq!(val.parse::<f32>().unwrap(), 3.14159f32);
    
    let val = String::from("Hello!");
    assert_eq!(val.parse::<String>().unwrap(), "Hello!".to_string());
}

但我需要这样的东西:

fn main() {
    let val = String::from("54");
    assert_eq!(val.generic_parse().unwrap(), 54i32); // or 54i16 or 54 u32 or etc ...
    
    let val = String::from("3.14159");
    assert_eq!(val.generic_parse().unwrap(), 3.14159f32);
    
    let val = String::from("Hello!");
    assert_eq!(val.generic_parse().unwrap(), "Hello!".to_string());
}

这样的东西有合适的板条箱吗?我不想无数次重新发明轮子。

编辑

这才是我真正想做的:

struct MyStruct<T> {
    generic_val: T,
}

fn main() {
    let val = String::from("54");
    let b = MyStruct {generic_val: val.parse().unwrap()};
    
    let val = String::from("3.14159");
    let b = MyStruct {generic_val: val.parse().unwrap()};
}

错误:

error[E0282]: type annotations needed for `MyStruct<T>`
 --> src/main.rs:7:13
  |
7 |     let b = MyStruct {generic_val: val.parse().unwrap()};
  |         -   ^^^^^^^^ cannot infer type for type parameter `T` declared on the struct `MyStruct`
  |         |
  |         consider giving `b` the explicit type `MyStruct<T>`, where the type parameter `T` is specified

【问题讨论】:

  • 这里的问题是assert_eq! 而不是parse。 Rust 编译器能够在你不告诉它的情况下弄清楚parse 应该采用什么类型参数。问题是你必须告诉它,所有assert_eq! 要求的是它的参数类型与== 相当,而不是它们相同。那么这正是你使用它的方式,或者如果我建议一个专注于assert_eq! 的修复程序,你会说这在你的真实代码中不起作用并且你需要一些不同的东西吗?
  • 啊,我只是以assert_eq! 为例。在实际代码中,此示例中的val 将被复制到结构中的泛型中。 let a = MyStruct {generic_val: val.copy(), ...etc 或类似的东西
  • 好的,那么问题就变成了:为什么编译器不能找出MyStruct 的类型参数? certainly possibleT 的选择推迟到创建MyStruct&lt;T&gt; 之后。你能创建一个minimal reproducible example吗?
  • 添加到主要问题中。
  • 我不认为你想要什么是可能的。你必须以一种或另一种方式告诉 Rust 你希望解析什么类型;您不能只说“将其解析为 something 并告诉我它是什么”。

标签: rust


【解决方案1】:

您需要基于正确的 Enum 类型并为其实现 FromStr。像这样。

#[derive(PartialEq, Debug)]
enum Val {
    Isize(isize),
    F64(f64),
}

impl core::str::FromStr for Val {
    type Err = & 'static str;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match (s.parse::<isize>(), s.parse::<f64>()) {
            (Ok(i),_) => Ok(Val::Isize(i)),
            (Err(_), Ok(f)) => Ok(Val::F64(f)),
            (Err(_), Err(_)) => Err("neither parser worked"),
        }
    }
}

 fn main() {
    assert_eq!("34".parse(), Ok(Val::Isize(34)));
    assert_eq!("12.3".parse(), Ok(Val::F64(12.3)));
    assert!("wrong".parse::<Val>().is_err());
}

【讨论】:

    【解决方案2】:

    Rust 是一种静态类型语言。这意味着编译器需要在编译时知道变量的类型。从那里可以采取三种方式:

    • 如果您的字符串在编译时已知,那么您不妨将它们替换为代码中的文字值(例如,"54"54)。
    • 如果您有其他方法可以在编译时知道给定字符串应该解析为什么类型,那么您可以在解析字符串时指定适当的类型:let a = "54".parse::&lt;i32&gt;().unwrap()
    • 如果您的字符串仅在运行时已知并且您想自动检测类型,那么您需要使用某种枚举值,将类型与值一起存储在程序中:
    use std::str::FromStr;
    enum Value {
        I32 (i32),
        F32 (f32),
        String (String),
    }
    
    impl Value {
        fn new (s: &str) -> Value {
            if let Ok (v) = s.parse::<i32>() {
                Value::I32 (v)
            } else if let Ok (v) = s.parse::<f32>() {
                Value::F32 (v)
            } else {
                Value::String (s.into())
            }
        }
    }
    

    这样,您的其余代码将有办法知道检测到的类型并相应地调整其处理。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-09-21
      • 2019-02-15
      • 2012-12-01
      • 2017-06-23
      • 1970-01-01
      • 1970-01-01
      • 2017-11-20
      • 1970-01-01
      相关资源
      最近更新 更多