0
0
Lập trình
Hưng Nguyễn Xuân 1
Hưng Nguyễn Xuân 1xuanhungptithcm

Rust cho Người Mới: 8 Mẹo Thực Hành Để Bắt Đầu

Đăng vào 6 tháng trước

• 8 phút đọc

Giới thiệu

Tôi đã học và viết Rust khoảng 7 năm nay, nhưng tôi vẫn nhớ cảm giác khó khăn của người mới bắt đầu khi mọi thứ có vẻ rất khó khăn và tôi liên tục phải chiến đấu với trình biên dịch ngay cả khi thực hiện những điều cơ bản. Bắt đầu với Rust có thể là một thách thức, nhưng qua việc dạy cho nhiều người mới trong vài năm qua, tôi đã thấy một số cạm bẫy hoặc mẫu phổ biến mà bạn có thể bắt đầu sử dụng ngay hôm nay để cải thiện quá trình học tập của mình.

Mẹo 1: Sử dụng unwrap cho Options và Results một cách thoải mái ban đầu

Options và Results là điều mới mẻ đối với những ai đến từ ngôn ngữ không có chúng. Hãy tập trung vào việc làm cho mọi thứ hoạt động trước bằng cách unwrap để làm cho mã của bạn hoạt động. Sau đó, hãy tìm hiểu cách xử lý chúng theo cách thức idiomatic sau. Bạn có thể đang viết một số mã tạm thời ban đầu, cố gắng làm cho một cái gì đó hoạt động, vì vậy việc xử lý lỗi sẽ không phải là ưu tiên hàng đầu, vì vậy tôi khuyên bạn nên unwrap thoải mái, sau đó thực hiện một lần nữa để cải thiện việc xử lý lỗi, như sau.

rust Copy
impl Database {
    pub fn find_student(&self, name: &str) -> Option<&Student> {
        /// ...
    }
}

pub fn main() {
    // Tải cơ sở dữ liệu với tất cả sinh viên 
    let database = Database::new();

    // Tìm một sinh viên cụ thể và in id của họ
    let student_id = database.find_student("Bob").unwrap().id;
    println!("Id của Bob là {}", student_id);
}

Sau đó, trong lần lặp thứ hai, mã này trở thành như sau:

Khi mã của bạn hoạt động và bạn hiểu rõ hơn về các trường hợp lỗi, hãy chuyển đổi từ unwrap() trong mã sản xuất nơi mà những panic không mong đợi có thể làm hỏng ứng dụng của bạn - đó là lúc việc xử lý lỗi đúng cách với match, if let, hoặc toán tử ? trở nên quan trọng cho tính ổn định. Dưới đây là chúng ta sử dụng match.

rust Copy
pub fn main() {
    // Tải cơ sở dữ liệu với tất cả sinh viên 
    let database = Database::new();

    // Tìm một sinh viên cụ thể và in id của họ
    match database.find_student("Bob") {
        Some(bob) => println!("Id của Bob là {}", bob.id);
        None => println!("Không tìm thấy Bob");
    }
}

Mẹo 2: Học sự khác biệt giữa String và &str

Điều này là cơ bản để hiểu, và đừng ngại sử dụng String chỉ trong giai đoạn đầu mặc dù nó có thể không hiệu quả. Bạn có thể tối ưu hóa sau khi hiểu rõ hơn các khái niệm.

Hiểu về các slice cũng sẽ giúp ở đây - các slice thực chất là các tham chiếu mượn vào các chuỗi dữ liệu liên tiếp. Hãy nghĩ về &str như một slice chuỗi tham chiếu đến dữ liệu chuỗi hiện có mà không sở hữu nó. Ví dụ, &s[0..5] tạo ra một slice tham chiếu đến 5 ký tự đầu tiên của chuỗi s mà không sao chép chúng. Khái niệm này áp dụng cho cả chuỗi và mảng/vector (nơi mà &[T] là một slice mảng). Hãy bắt đầu bằng cách sử dụng chúng để tham chiếu các phần của dữ liệu hiện có, và bạn sẽ từ từ hiểu cách chúng giúp tiết kiệm bộ nhớ.

rust Copy
fn main() {
    // String - sở hữu, được phân bổ trên heap, có thể thay đổi
    let mut owned_string = String::from("Hello");
    owned_string.push_str(" World"); // Có thể sửa đổi

    // &str - tham chiếu mượn đến dữ liệu chuỗi, không thay đổi
    let string_slice: &str = "Hello World"; // Chuỗi hằng
    let borrowed_slice: &str = &owned_string; // Mượn từ String

    // Hàm nhận &str có thể chấp nhận cả hai
    print_message(&owned_string); // String tự động chuyển đổi sang &str
    print_message(string_slice);  // &str được truyền trực tiếp
}

fn print_message(msg: &str) {
    println!("{}", msg);
}

Mẹo 3: Clone thoải mái (nhưng không Copy)

Ban đầu bạn sẽ nhận được nhiều thông báo từ trình biên dịch vì bạn đang di chuyển các đối tượng mà không biết rằng bạn đang di chuyển chúng. Thêm một chú thích Clone, gọi .clone() và tiếp tục với vấn đề tiếp theo! Sau đó, trong lần lặp thứ hai, quay lại và tối ưu hóa.

rust Copy
#[derive(Clone)]
struct Student {
    name: String,
    id: u32,
}

fn process_student(student: Student) {
    println!("Đang xử lý sinh viên: {}", student.name);
}

fn main() {
    let alice = Student {
        name: "Alice".to_string(),
        id: 12345,
    };

    process_student(alice.clone()); // Clone để không di chuyển alice
    println!("Alice vẫn có sẵn: {}", alice.name); // Điều này hoạt động!
}

Mẹo 4: Tránh lưu trữ tham chiếu trong Structs

Ngay khi bạn làm điều này, bạn sẽ cần phải tìm hiểu về lifetimes và nếu bạn vẫn đang học các khái niệm cơ bản, tôi khuyên bạn nên tránh điều này. Chỉ cần lưu trữ các kiểu sở hữu, và truyền mọi thứ còn lại như các tham số cho các hàm.

rust Copy
// TRÁNH
struct Course<'a> {
    student: &'a Student, // Mượn, cần explicit lifetimes
}

// TỐT
struct Course {
    student: Student, // Sở hữu dữ liệu
}

Mẹo 5: Tránh danh sách liên kết và cây

Ngay khi bạn bắt đầu với điều này, bạn sẽ cần phải hiểu về mượn và Box/Rc/Arc mà bạn sẽ gặp khó khăn khi mới bắt đầu. Khi bạn cảm thấy thoải mái với các khái niệm cơ bản, thì hãy đi sâu hơn vào những cấu trúc dữ liệu nâng cao này. Có một cuốn sách hoàn toàn về việc viết nhiều loại danh sách liên kết: "Học Rust Với Quá Nhiều Danh Sách Liên Kết" mà tôi khuyên bạn nên đọc chỉ sau khi bạn học ít nhất một số loại con trỏ cơ bản của Rust.

Mẹo 6: Xem xét Thứ tự và Sử dụng Khối Phạm vi

Đây là mẹo yêu thích của tôi, nếu bạn dự định thực hiện nhiều biến đổi, hoặc đọc, ghi rồi đọc lại, thứ tự cộng với việc tạo ra một khối phạm vi mới cho mỗi có thể cứu bạn vì nó giải phóng quyền mượn.

rust Copy
struct Student {
    name: String,
    grade: u8,
}

impl Student {
    pub fn new(name: &str) -> Self {
        Self {
            name: name.to_string(),
            grade: 100,
        }
    }

    pub fn grade_mut(&mut self) -> &mut u8 {
        &mut self.grade
    }

    pub fn name(&self) -> &str {
        &self.name
    }
}

fn main() {
    let mut student = Student::new("Alice");

    {
        let grade = student.grade_mut();
        *grade += 1;
    } // quyền mượn biến grade bị loại bỏ ở đây

    let name = student.name();  // Bây giờ điều này hoạt động
    println!("Tên: {}", name);
}

/// HOẠT ĐỘNG!!

So sánh điều này với phiên bản có vấn đề:

rust Copy
fn main() {
    let mut student = Student::new("Alice");

    let grade = student.grade_mut(); // Mượn biến thay đổi đầu tiên
    let name = student.name();       // Cố gắng mượn không thay đổi ngay lập tức
    *grade += 1;                     // Vẫn đang sử dụng quyền mượn thay đổi

    println!("Tên: {}, Điểm: {}", name, grade);
}

// Lỗi: không thể mượn `student` như một tham chiếu không thay đổi vì nó cũng đang được mượn như một tham chiếu thay đổi

Mẹo 7: Sử dụng Option::take() để Di chuyển Giá trị An toàn

Nếu bạn cần di chuyển một giá trị ra khỏi một struct, Option<T>::take() sẽ ngăn chặn các xung đột khi mượn:

rust Copy
struct Database {
    student: Option<Student>,
}

impl Database {
    fn remove_student(&mut self) -> Option<Student> {
        self.student.take() // ✅ Thay thế bằng None, tránh vấn đề mượn
    }
}

let mut db = Database { student: Some(Student::new("Charlie")) };
let removed_student = db.remove_student(); // ✅ Sinh viên được di chuyển an toàn ra ngoài

Mẹo 8: Truyền Dữ liệu như Các Tham số Hàm Thay vì Lưu Trữ

Bước bổ sung để lưu trữ các giá trị sở hữu thực sự không phải là lưu trữ giá trị nào cả. Thay vì giữ một tham chiếu/giá trị sở hữu bên trong một struct, hãy truyền dữ liệu khi cần thiết, và khi nó hợp lý. Việc truyền các tham chiếu xung quanh cũng dễ dàng hơn mà không phải xử lý các lifetime.

rust Copy
struct Course;

impl Course {
    fn process(student: &mut Student) {
        println!("{} đã được xử lý!", student.name);
    }
}

let alice = Student::new("Alice");
Course::process(&mut alice); // ✅ Truyền một tham chiếu thay vì lưu trữ

Kết luận

Những mẹo thực hành này đã giúp tôi ban đầu, và khi tôi cảm thấy thoải mái hơn với ngôn ngữ, tôi đã bắt đầu đi sâu vào từng chủ đề này và học cách thực hiện mọi thứ theo cách idiomatic và hiệu quả nhất.

Hy vọng rằng nó đã giúp bạn học được 1% Rust hôm nay, tôi viết một bản tin như thế này mỗi thứ Năm, hãy đăng ký để nhận được nó, và theo dõi tôi trên Bluesky 🦋 🦀.

Gợi ý câu hỏi phỏng vấn
Không có dữ liệu

Không có dữ liệu

Bài viết được đề xuất
Bài viết cùng tác giả

Bình luận

Chưa có bình luận nào

Chưa có bình luận nào