Rust: FromStr trait

FromStr là một trait để khởi tạo instance từ string trong Rust, nó tương đương abstract class nếu bạn có background OOP.

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

Thường phương thức from_str của FromStr thường được ngầm định sử dụng thông qua phương thức parse của str. Ví dụ:

// Thay vì
let one = u32::from_str("1");

// thì sử dụng phương thức parse
let one: u32 = "1".parse().unwrap();
assert_eq!(1, one);

// parse() sử dụng turbofish ::<>
let two = "2".parse::<u32>();
assert_eq!(Ok(2), two);

let nope = "j".parse::<u32>();
assert!(nope.is_err());

parse là một phương thức general nên thường được sử dụng với kiểu dữ liệu như trên hoặc sử dụng turbofish ::<> để thuật toán inference có thể hiểu để parse thành đúng kiểu bạn cần.

Parse str to Struct

Bạn có 1 struct và muốn parse 1 str thành struct đó, bạn sẽ cần impl trait FromStr

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

#[derive(Debug, PartialEq)]
struct Point {
  x: i32,
  y: i32
}

impl FromStr for Point {
  type Err = ParseIntError;

  fn from_str(s: &str) -> Result<Self, Self::Err> {
    let coords: Vec<&str> = s.trim_matches(|p| p == '(' || p == ')' )
                               .split(',')
                               .collect();

    let x_fromstr = coords[0].parse::<i32>()?;
    let y_fromstr = coords[1].parse::<i32>()?;

    Ok(Point { x: x_fromstr, y: y_fromstr })
  }
}

// Có nhiều cách
let p: Point = "(1,2)".parse();
let p = "(1,2)".parse::<Point>();
let p = Point::from_str("(1,2)");

assert_eq!(p.unwrap(), Point{ x: 1, y: 2} )

Parse str to Enum

Một điều mình thấy để code dễ đọc, dễ maintain hơn là chúng ta nên tránh sử dụng stringly-typed apis. Ví dụ như:

fn print(color: &str, text: &str) { ... }
print("Foobar", "blue");

Thay vì đó mà hãy sử dụng enum:

enum Color { Red, Green, CornflowerBlue }

fn print(color: Color, text: &str) { ... }
print(Green, "duyet");

Cũng nên hạn chế sử dụng quá nhiều Boolean, thực tế Boolean cũng chỉ là

enum bool { true, false }

Thay vào đó hãy tự định nghĩa enum cho các ngữ cảnh khác nhau để code dễ đọc hơn:

enum EnvVars { Clear, Inherit }
enum DisplayStyle { Color, Monochrome }

Chúng ta implement std::str::FromStr trait như sau:

use std::str::FromStr;

#[derive(Debug, PartialEq)]
enum Color {
  Red,
  Green,
  Blue
}

impl FromStr for Color {
  type Err = ();

  fn from_str(input: &str) -> Result<Color, Self::Err> {
    match input {
      "red"   => Ok(Color::Red),
      "green" => Ok(Color::Green),
      "blue"  => Ok(Color::Blue),
      _       => Err(()),
    }
  }
}

let c: Color = "red".parse().unwrap();
assert_eq!(c, Color::Red);

References

Rust 🦀Rust, Vietnamese, Rust Tiếng Việt, Rust Basic