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

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

Những mẩu tin ngắn hàng ngày dành cho bạn
  • Một phần mềm giúp chuyển đổi văn bản thành giọng nói do một lập trình viên người Việt làm ra - J2TEAM - Text to Speech (Free). Bạn có thể chuyển đổi hàng chục ngôn ngữ sang hàng chục giọng đọc tự nhiên khác nhau. Điều đặc biệt là nó miễn phí.

    Đánh giá sơ bộ thì chuyển đổi văn bản dài hoặc văn bản bằng tiếng Việt thuần thì rất tốt. Còn dính thêm các từ tiếng Anh thì nó đọc hơi buồn cười 😅

    » Xem thêm
  • Quá ghê ghớm, Codeium - vốn được biết đến như một đối thủ của Github Copilot, khi nó cho người dùng dùng miễn phí không giới hạn. Mới đây họ giới thiệu thêm Windsurf Editor - không chỉ còn là VSCode Extentions nữa mà là một trình Editor luôn - cạnh tranh trực tiếp với Cursor. Và điểm chính là nó... hoàn toàn miễn phí 🫣.

    » Xem thêm
  • Tin vui đầu này, Github Copilot đã chính thức có bản Free cho tất cả mọi người.

    Github Copilot là một trợ lý AI code cùng chúng ta, nó có thể tự động hoàn thành mã, trò chuyện hoặc sửa lỗi. Hiện đang hỗ trợ nhiều trình soạn thảo, IDE phổ biến như VSCode, JetBrains, XCode...

    Phiên bản miễn phí đang bị giới hạn 2000 Suggestions, và khoảng 50 requests đến tính năng Chat hàng tháng. Sau đó bạn có thể nâng cấp lên các phiên bản cao cấp hơn với giá từ 10$.

    Theo đánh giá của mình thì Copilot rất tốt và xứng đáng trong tầm giá, đang dùng hàng ngày 😄

    » 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

5 bài học sâu sắc

Mỗi sản phẩm đi kèm với những câu chuyện. Thành công của người khác là nguồn cảm hứng cho nhiều người theo sau. 5 bài học rút ra được đã thay đổi con người tôi mãi mãi. Còn bạn? Hãy bấm vào ngay!

Mỗi sản phẩm đi kèm với những câu chuyện. Thành công của người khác là nguồn cảm hứng cho nhiều người theo sau. 5 bài học rút ra được đã thay đổi con người tôi mãi mãi. Còn bạn? 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.
Author

Xin chào, tôi tên là Hoài - một anh Dev kể chuyện bằng cách viết ✍️ và làm sản phẩm 🚀. Với nhiều năm kinh nghiệm lập trình, tôi đã đóng góp một phần công sức cho nhiều sản phẩm mang lại giá trị cho người dùng tại nơi đang làm việc, cũng như cho chính bản thân. Sở thích của tôi là đọc, viết, nghiên cứu... Tôi tạo ra trang Blog này với sứ mệnh mang đến những bài viết chất lượng cho độc giả của 2coffee.dev.Hãy theo dõi tôi qua các kênh LinkedIn, Facebook, Instagram, Telegram.

Bạn thấy bài viết này có ích?
Không

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