1 tháng học Rust - Enums và Pattern Matching

1 tháng học Rust - Enums và Pattern Matching

Tin ngắn hàng ngày dành cho bạn
  • Bắt đầu kỳ nghỉ tết rồi nên mình cũng không đăng bài nữa. Hẹn gặp lại các bạn qua tết nha 😁

    » Xem thêm
  • Tiếp tục về jj. Đang thắc mắc là nó mới thế liệu có ai làm mấy phần mềm dạng GUI cho dễ nhìn chưa. Kiểu giống như git thì có quá nhiều rồi không đếm xuể.

    May quá, tác giả tổng hợp lại luôn rồi Community-built tools around Jujutsu 🥳

    » Xem thêm
  • Turso thông báo rằng họ đang viết lại SQLite bằng Rust. Thế là lại có thêm một bằng chứng nữa cũng cố cho câu nói Rust đang "tái định nghĩa" lại nhiều thứ.

    Nhưng nguyên nhân sâu xa mới thú vị. Tại sao họ lại làm vậy? Ai cũng biết SQLite là nguồn mở, ai cũng có thể tạo bản sao (fork) để chỉnh sửa lại theo ý mình. Lẽ nào nhóm của Turso không thích hoặc không tin vào C - vốn là ngôn ngữ dùng để cấu thành SQLite.

    Mình xin kể chuyện một chút. Turso là một bên cung cấp dịch vụ máy chủ cơ sở dữ liệu dựa trên SQLite, họ đã thực hiện một vài tùy chỉnh trên bản sao của SQLite để phục vụ cho mục đích của mình, gọi nó là libSQL. Họ "hào phóng" cho cộng đồng đóng góp thoải mái.

    Quay trở lại SQLite là mã nguồn mở chứ không phải là đóng góp mở. Chỉ có một nhóm người đứng đằng sau duy trì mã nguồn này, và họ không tiếp nhận yêu cầu kéo (pull request) từ những người khác. Đồng nghĩa mọi thay đổi hoặc tính năng đều là của nhóm người này tạo ra. Có vẻ như SQLite rất phổ biến nhưng cộng đồng không thể làm điều mà họ muốn là đóng góp cho sự phát triển của nó.

    Chúng ta biết rằng hầu hết ứng dụng mã nguồn mở thường đi kèm với một thư mục "tests" với các bài kiểm tra rất nghiêm ngặt. Điều đó giúp cho sự cộng tác trong phát triển trở nên dễ dàng hơn. Nếu muốn chỉnh sửa hoặc thêm một tính năng mới, trước hết bạn cần phải đảm bảo sự thay đổi vượt qua được tất cả bài kiểm tra. Nhiều thông tin cho rằng SQLite không công khai bộ kiểm tra này. Điều này vô tình gây khó khăn cho những ai muốn chỉnh sửa mã nguồn. Vì họ không chắc chắn rằng liệu triển khai mới của mình có phù hợp với những tính năng cũ hay không.

    tursodatabase/limbo là dự án viết lại SQLite bằng Rust đã nhắc đến ở đầu bài. Họ nói rằng nó hoàn toàn tương thích với SQLite và nguồn mở hoàn toàn. limbo đang trong giai đoạn hoàn thiện. Chúng ta hãy chờ xem kết quả trong tương lai thế nào nhé. Bài viết chi tiết tại Introducing Limbo: A complete rewrite of SQLite in Rust.

    » Xem thêm

Vấn đề

Về enums - một cấu trúc dữ liệu liệt kê mà có thể ai cũng biết. Enums cho phép xác định một loại bằng cách liệt kê các biến thể có thể có của nó. Trong mỗi ngôn ngữ lập trình, enums có cách khai báo và sử dụng khác nhau. Đối với Rust, enums được sử dụng khá thường xuyên vì nhiều lợi ích mà nó mang lại.

Bài viết ngày hôm nay chúng ta sẽ cùng nhau tìm hiểu về enums, xem nó có gì hay ho hơn so với các ngôn ngữ khác không nhé!

Enums

Một enums được khai báo như sau:

enum IpAddrKind {
    V4,  
    V6,  
}

Sau đó có thể sử dụng:

let four = IpAddrKind::V4;
let six = IpAddrKind::V6;

Enums trong Rust cũng lưu trữ được dữ liệu.

enum IpAddr {
    V4(String),  
    V6(String),  
}

let home = IpAddr::V4(String::from("127.0.0.1"));
let loopback = IpAddr::V6(String::from("::1"));

Thậm chí, chúng ta còn có thể khai báo thêm phương thức cho nó.

enum IpAddr {
    V4(String),  
    V6(String),  
}

impl IpAddr {
    fn call(&self) {
        println!("Hello from IpAddr!");
    }
}

let home = IpAddr::V4(String::from("127.0.0.1"));
home.call();

Option là một enums được định nghĩa sẵn ở trong Rust, nó có một sứ mệnh quan trọng trong việc xử lý dữ liệu null.

Một Option có dạng Generic như sau.

enum Option<T> {
    None,  
    Some(T),  
}

Nhiều ngôn ngữ lập trình khác, chúng ta quá quen thuộc với kiểu dữ liệu null đại diện cho việc không có giá trị nào. Rust không có kiểu dữ liệu null, hầu hết trường hợp cần sử dụng null thì sẽ chuyển qua sử dụng enums Option.

None đại diện cho “không có gì” còn Some đại diện cho sự hiện hữu của một dạng dữ liệu nào đó. Lấy ví dụ.

let none: Option<i8> = None;
let five: Option<i8> = Some(5);
let six: Option<i8> = Sone(6);

Và dĩ nhiên chúng ta không thể thực hiện các phép tính trực tiếp trên kiểu dữ liệu Option này một cách bình thường.

// error
let sum = none + five;

// error
let sum = five + six;

Vậy thì làm cách nào để sử dụng được enums nói chung cũng như Option nói riêng?

Matching

Hầu hết việc xác định enums nhằm mục đích phân loại và xử lý dữ liệu theo các dạng được định nghĩa từ trước, chính vì thế Rust có cấu trúc xử lý loại rất mạnh mẽ là match hay còn gọi là “khớp mẫu”.

enum Coin {
    Penny,  
    Nickel,  
    Dime,  
    Quarter,  
}

fn value_in_cents(coin: Coin) -> u8 {
    match coin {
        Coin::Penny => 1,  
        Coin::Nickel => 5,  
        Coin::Dime => 10,  
        Coin::Quarter => 25,  
    }
}

Hàm value_in_cents nhận vào một enums có kiểu Coin từ đó trả về giá trị tương ứng với tên của các đồng xu bằng cách sử dụng cú pháp match.

Đối với Option, chúng ta cũng sử dụng match để xử lý các trường hợp NoneSome, cũng như thực hiện các phép tính cơ bản mà Some nắm giữ như ví dụ ở đầu bài viết.

fn plus_one(x: Option<i32>) -> Option<i32> {
    match x {
        None => None,  
        Some(i) => Some(i + 1),  
    }
}

let five = Some(5);
let six = plus_one(five);
let none = plus_one(None);

Hàm plus_one nhận vào một Option<i32> và cộng thêm 1 vào giá trị của nó. Bằng cách khớp mẫu, hàm trả về None nếu như x là None, ngược lại nếu x là loại Some, tức là nó có giá trị thì sẽ trả về giá trị Some mới bằng cách cộng thêm 1.

Cho dễ hình dung, match gần giống với lệnh swich…case trong JavaScript. Trong khi switch đưa ra một giá trị khi nào khớp với giá trị trong case thì hàm xử lý đó ngay lập tức được thực thi. Nếu như switch…case có trường hợp default, nghĩa là khi các giá trị trong tất cả case không khớp thì cuối cùng hàm xử lý default được thực hiện. Trong Rust cũng có, người ta gọi là “Placeholder” hay “trình giữ chỗ”.

let dice_roll = 9;
match dice_roll {
    3 => add_fancy_hat(),  
    7 => remove_fancy_hat(),  
    other => move_player(other),  
}

fn add_fancy_hat() {}
fn remove_fancy_hat() {}
fn move_player(num_spaces: u8) {}

Quay trở lại với trường hợp sử dụng Option, đôi khi chúng ta không quan tâm đến None lắm mà chỉ đơn giản là muốn thao tác với dữ liệu nếu có của Some. Ví dụ dưới đây luôn phải khai báo thêm một trình giữ chỗ để “không phải làm gì”.

let config_max = Some(3u8);
match config_max {
    Some(max) => println!("The maximum is configured to be {}", max),  
    _ => (),  
}

Điều này lặp lại một cách nhàm chán và thừa thãi, thay vào đó, Rust gợi ý chúng ta sử dụng kết hợp iflet để chỉ xử lý riêng cho một trường hợp cụ thể của một enums.

let config_max = Some(3u8);
if let Some(max) = config_max {
    println!("The maximum is configured to be {}", max);
}

Cuối cùng, vẫn có một lưu ý quan trọng về quyền sở hữu khi khớp mẫu khi Some giữ các giá trị tham chiếu. Bạn đọc có thể tham khảo thêm tại The Rust Programming Language - How Matches Interact with Ownership.

Cao cấp
Hello

Bí mật ngăn xếp của Blog

Là một lập trình viên, bạn có tò mò về bí mật công nghệ hay những khoản nợ kỹ thuật về trang blog này? Tất cả bí mật sẽ được bật mí ngay bài viết dưới đây. Còn chờ đợi gì nữa, hãy bấm vào ngay!

Là một lập trình viên, bạn có tò mò về bí mật công nghệ hay những khoản nợ kỹ thuật về trang blog này? Tất cả bí mật sẽ được bật mí ngay bài viết dưới đây. Còn chờ đợi gì nữa, hãy bấm vào ngay!

Xem tất cả

Đăng ký nhận thông báo bài viết mới

hoặc
* Bản tin tổng hợp được gửi mỗi 1-2 tuần, huỷ bất cứ lúc nào.

Bình luận (1)

Nội dung bình luận...
Avatar
Ẩn danh1 năm trước
Mình cũng bắt đầu tìm hiểu về rust, bạn có muốn lập nhóm ngồi cf trao đổi để học không?
Trả lời
Avatar
Xuân Hoài Tống1 năm trước
Chào bạn, rất vui khi nhận được lời đề nghị của bạn. Mình viết ra series tự học Rust để tạo cam kết cũng như ghi chép lại những gì học được, qua đó tóm tắt lại một cách cô đọng cho người đọc tham khảo. Tuy nhiên thì mình có nhiều lý do để rất khó lập được nhóm và cùng nhau trao đổi, như công việc và gia đình. Nhưng không sao, mình vẫn có thời gian cuối ngày để viết blog cũng như rất sẵn sàng trao đổi với bạn (dù không được realtime cho lắm). Hoặc nếu bạn cũng viết blog thì có thể để lại địa chỉ để cùng nhau học hỏi thêm :D
Bấm hoặc cuộn mạnh để sang bài mới