< 返回版块

Mike Tang 发表于 2019-12-12 10:44

Tags:rust

Rust 中,有一个神奇的函数 parse。它定义在 std:str 下面。因为字符串中可以存放各种信息(如果加上序列化的话),对字符串的解析就显得特别重要,而且很通用。于是 Rust 就赋予了这个函数强大(几乎无限)的功能。

它的原型定义是:

pub fn parse<F>(&self) -> Result<F, <F as FromStr>::Err> where

    F: FromStr,

Parses this string slice into another type.

Because parse is so general, it can cause problems with type inference. As such, parse is one of the few times you'll see the syntax affectionately known as the 'turbofish': ::<>. This helps the inference algorithm understand specifically which type you're trying to parse into.

parse can parse any type that implements the FromStr trait.

把这个字符串切片解析成另外一种类型。

因为解析太普遍了,往往在类型推导的时候会产生问题。因此,parse 是你能看到的使用了turbofish语法(::<>)的少数几个场景之一,它帮助推导算法知道你想解析到什么类型上去。

parse 可以解析任何实现了 FromStr trait 的类型。

出错情况下,会返回类型:std::str::FromStr::Err。这是一个关联类型,在为目标类型实现 FromStr 的时候,确定具体类型。

FromStr 的定义是这样的:

pub trait FromStr {
    type Err;
    fn from_str(s: &str) -> Result<Self, Self::Err>;
}

下面我们就以示例来引导大家理解。

示例一:解析成整数

把字符串解析为整数,各种类型。

fn main() {
   let astr = "7";

   let n = astr.parse::<i32>().unwrap();
   println!("{:?}", n);

   let n = astr.parse::<i64>().unwrap();
   println!("{:?}", n);

   let n = astr.parse::<u32>().unwrap();
   println!("{:?}", n);

   let n = astr.parse::<u64>().unwrap();
   println!("{:?}", n);

   let astr = "7.42";
   let n: f32 = astr.parse().unwrap();
   println!("{:?}", n);

   let n: f64 = astr.parse().unwrap();
   println!("{:?}", n);
}

运行结果如下:

7
7
7
7
7.42
7.42

示例二:解析成整数,但是是自定义内容

比如解析 "<123>",这个需要自己实现 FromStr trait 来解析。

use std::str::FromStr;
use std::num::ParseIntError;

/// Parse str like this: "<123>" to 123:i32

#[derive(Debug, PartialEq)]
struct MyInt(i32);

impl FromStr for MyInt {
   type Err = ParseIntError;

   fn from_str(s: &str) -> Result<Self, Self::Err> {
       let astr = &s[1..s.len()-1];
       match astr.parse::<i32>() {
           Ok(n) => Ok(MyInt(n)),
           Err(e) => Err(e)
       }
   }
}

fn main() {
   let astr = "<142>";
   let n: MyInt = astr.parse().unwrap();
   println!("{:?}", n);
}

输出结果:

MyInt(142)

标准库中默认已实现 FromStr 的类型列表

https://doc.rust-lang.org/std/str/trait.FromStr.html

这里面有默认实现了FromStr的std中的类型的列表,罗列如下:

  • i8, i16, i32, i64, i128, isize
  • u8, u16, u32, u64, u128, usize
  • f32, f64
  • bool
  • char
  • String
  • IpAddr, Ipv4Addr, Ipv6Addr
  • SocketAddr, SocketAddrV4, SocketAddrV6
  • NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, NonZeroIsize
  • NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, NonZeroUsize
  • PathBuf
  • TokenStream

可以看到,还是不少的。


Rust核心团队对 parse 的设计,也成为了生态中的一种标准,影响了上层各种库的 API 设计风格。下面看一下一些常见的库。

示例三:解析成 Uuid

use uuid::Uuid;

fn main() {
   let astr = "97103fab-1e50-36b7-0c03-0938362b0809";

   let auuid = Uuid::parse_str(astr).unwrap();
   println!("{:?}", auuid);

   let auuid: Uuid = astr.parse().unwrap();
   println!("{:?}", auuid);
}

输出结果:

97103fab-1e50-36b7-0c03-0938362b0809
97103fab-1e50-36b7-0c03-0938362b0809

示例四:解析成 chrono库中的 NaiveDateTime

use chrono::NaiveDateTime;

fn main() {
   let astr = "2015-09-05 23:56:04";

   let ndt = NaiveDateTime::parse_from_str(astr, "%Y-%m-%d %H:%M:%S").unwrap();
   println!("{:?}", ndt);

   // This will cause error, for the reason of unknown format
   // let ndt: NaiveDateTime = astr.parse().unwrap();
   // println!("{:?}", ndt);

   // Let's give the default format.
   let astr = "2015-09-18T23:56:04";
   let ndt: NaiveDateTime = astr.parse().unwrap();
   println!("{:?}", ndt);

   let astr = "2015-09-18T23:56:04.07";
   let ndt: NaiveDateTime = astr.parse().unwrap();
   println!("{:?}", ndt);
}

输出结果:

2015-09-05T23:56:04
2015-09-18T23:56:04
2015-09-18T23:56:04.070

示例五:解析成 serde_json 的 Value

struct User {
   fingerprint: String,
   location: String,
}

fn main() {
   // The type of `j` is `&str`
   let j = "
       {
           \"fingerprint\": \"0xF9BA143B95FF6D82\",
           \"location\": \"Menlo Park, CA\"
       }";

   let u: User = serde_json::from_str(j).unwrap();
   println!("{:#?}", u);

   let u: Value = j.parse().unwrap();
   println!("{:#?}", u);

   //let u: User = j.parse().unwrap();
   //println!("{:#?}", u);

   // error[E0277]: the trait bound `User: std::str::FromStr` is not satisfied
   //  --> src/main.rs:24:21
   //   |
   // 24 |    let u: User = j.parse().unwrap();
   //   |                    ^^^^^ the trait `std::str::FromStr` is not implemented for `User`
}

输出结果:

User {
   fingerprint: "0xF9BA143B95FF6D82",
   location: "Menlo Park, CA",
}

Object(
   {
       "fingerprint": String(
           "0xF9BA143B95FF6D82",
       ),
       "location": String(
           "Menlo Park, CA",
       ),
   },
)

可以看到,都是一致的用法。

本文差不多到这里该结束了,高级的序列化/反序列化的工作,应该交给 Serde 等系列库来解决。更高级的解析器什么的,就需要 nom 等库出马了。

本文所有示例,皆放于仓库:

https://github.com/daogangtang/learn-rust/tree/master/01parse

评论区

写评论
Mark 2019-12-13 08:27

学习

1 共 1 条评论, 1 页