Result

Tương tự như Option. Một kết quả trả về (Result) của một function thường sẽ có hai trường hợp:

  • thành công (Ok) và trả về kết quả
  • hoặc lỗi (Err) và trả về thông tin lỗi.

Result là một phiên bản cao cấp hơn của Option. Nó mô tả lỗi gì đang xảy ra thay vì khả năng tồn tại giá trị hay không.

#![allow(unused)]
fn main() {
enum Result<T, E> {
  Ok(T),
  Err(E),
}
}

Ví dụ

fn get_id_from_name(name: &str) -> Result<i32, &str> {
    if !name.starts_with('d') {
        return Err("not found");
    }

    Ok(123)
}

fn main() -> Result<(), &'static str> {
    let name = "duyet";

    match get_id_from_name(name) {
        Ok(id) => println!("User = {}", id),
        Err(e) => println!("Error: {}", e),
    };

    Ok(())
}

Như bạn thấy thì main() cũng có thể return về Result<(), &'static str>

.unwrap()

Ví dụ trên nhưng sử dụng .unwrap() , chủ động panic (crash) dừng chương trình nếu gặp lỗi.

fn main() -> Result<(), &'static str> {
  let who = "duyet";
  let age = get_age(who).unwrap();
  println!("{} is {}", who, age);

  Ok(())
}

.expect()

Giống như unwrap(): chủ động panic (crash) dừng chương trình nếu gặp lỗi và kèm theo message. Sẽ rất có ích, nhất là khi có quá nhiều unwrap, bạn sẽ không biết nó panic ở đâu.

fn main() -> Result<(), &'static str> {
  let who = "ngan";
  let age = get_age(who).expect("could not get age");
  println!("{} is {}", who, age);

  Ok(())
}

Xem thêm mọi method khác của Result tại đây.

Convert Result sang Option

Đôi khi bạn sẽ cần convert từ

  • Ok(v) sang Some(v), hoặc
  • Err(e) sang Some(e)

.ok()

// .ok(v) = Some(v)
let x: Result<u32, &str> = Ok(2);
assert_eq!(x.ok(), Some(2));

let x: Result<u32, &str> = Err("Nothing here");
assert_eq!(x.ok(), None);

.err()

// .err()
let x: Result<u32, &str> = Ok(2);
assert_eq!(x.err(), None);

let x: Result<u32, &str> = Err("Nothing here");
assert_eq!(x.err(), Some("Nothing here"));

Toán tử ?

Khi viết code mà có quá nhiều functions trả về Result, việc handle Err sẽ khá nhàm chán. Toán tử chấm hỏi ? cho phép dừng function tại vị trí đó và return cho function cha nếu Result ở vị trí đó là Err.

Nó sẽ thay thế đoạn code sau:

use std::fs::File;
use std::io::prelude::*;
use std::io;

struct Info {
  name: String,
  age: i32,
  rating: i32,
}

fn write_info(info: &Info) -> io::Result<()> {
  // Early return on error
  let mut file = match File::create("my_best_friends.txt") {
    Err(e) => return Err(e),
    Ok(f) => f,
  };
  if let Err(e) = file.write_all(format!("name: {}\n", info.name).as_bytes()) {
    return Err(e)
  }
  if let Err(e) = file.write_all(format!("age: {}\n", info.age).as_bytes()) {
    return Err(e)
  }
  if let Err(e) = file.write_all(format!("rating: {}\n", info.rating).as_bytes()) {
    return Err(e)
  }
  Ok(())
}

thành

use std::fs::File;
use std::io::prelude::*;
use std::io;

struct Info {
  name: String,
  age: i32,
  rating: i32,
}

fn write_info(info: &Info) -> io::Result<()> {
  let mut file = File::create("my_best_friends.txt")?;
  // Early return on error
  file.write_all(format!("name: {}\n", info.name).as_bytes())?;
  file.write_all(format!("age: {}\n", info.age).as_bytes())?;
  file.write_all(format!("rating: {}\n", info.rating).as_bytes())?;
  Ok(())
}

Gọn đẹp hơn rất nhiều.

Toán tử ? sẽ unwrap giá trị Ok, hoặc return giá trị Err ở vị trí gần toán tử đó.

? chỉ có thể được dùng trong function có kiểu dữ liệu trả về là Result.