body {
font-family: ‘Segoe UI’, Tahoma, Geneva, Verdana, sans-serif;
line-height: 1.6;
margin: 0 auto;
max-width: 800px;
padding: 20px;
color: #333;
background-color: #f9f9f9;
}
h1, h2, h3 {
color: #2c3e50;
}
h1 {
text-align: center;
margin-bottom: 30px;
}
h2 {
border-bottom: 2px solid #3498db;
padding-bottom: 10px;
margin-top: 40px;
}
h3 {
color: #34495e;
margin-top: 30px;
}
p {
margin-bottom: 15px;
}
ul {
list-style-type: disc;
margin-left: 20px;
margin-bottom: 15px;
}
li {
margin-bottom: 8px;
}
a {
color: #3498db;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
pre {
background-color: #ecf0f1;
padding: 15px;
border-radius: 5px;
overflow-x: auto;
font-family: ‘Fira Code’, ‘Cascadia Code’, ‘Consolas’, monospace;
font-size: 0.9em;
margin-bottom: 20px;
}
code {
font-family: ‘Fira Code’, ‘Cascadia Code’, ‘Consolas’, monospace;
background-color: #e0e6e7;
padding: 2px 4px;
border-radius: 3px;
}
strong {
color: #c0392b;
}
Chào mừng bạn đến với hành trình khám phá Rust – ngôn ngữ lập trình nổi tiếng với hiệu suất cao và an toàn bộ nhớ vượt trội! Trong loạt bài viết Lập trình Rust cơ bản này, chúng ta sẽ cùng nhau đi sâu vào từng khía cạnh nhỏ nhất để xây dựng một nền tảng vững chắc. Hôm nay, chủ đề của chúng ta là một kiểu dữ liệu vô cùng quan trọng và cơ bản: Mảng (Array).
Mảng là một trong những cấu trúc dữ liệu đầu tiên mà bất kỳ lập trình viên nào cũng cần nắm vững. Trong Rust, mảng không chỉ đơn thuần là một tập hợp các giá trị, mà còn được thiết kế với sự an toàn và hiệu quả làm trọng tâm. Hãy cùng tìm hiểu chi tiết về cách mảng hoạt động trong Rust nhé!
1. Mảng là gì trong Rust?
Trong Rust, mảng là một tập hợp các phần tử có cùng một kiểu dữ liệu, được lưu trữ liền kề trong bộ nhớ và có kích thước cố định. Điều này có nghĩa là một khi bạn đã khai báo một mảng, số lượng phần tử của nó sẽ không thể thay đổi trong suốt thời gian chạy của chương trình.
- Đồng nhất kiểu dữ liệu: Tất cả các phần tử trong mảng phải có cùng một kiểu (ví dụ: tất cả là số nguyên
i32, hoặc tất cả là chuỗi ký tự&str). - Kích thước cố định: Kích thước của mảng phải được biết tại thời điểm biên dịch. Đây là điểm khác biệt chính giữa mảng và
Vector(sẽ được tìm hiểu sau), vốn có kích thước động. - Lưu trữ liền kề: Các phần tử của mảng được lưu trữ cạnh nhau trong bộ nhớ, giúp việc truy cập rất nhanh và hiệu quả.
Nhờ những đặc tính này, mảng trong Rust thường được sử dụng cho các tập dữ liệu nhỏ mà bạn biết chính xác kích thước của chúng từ trước, mang lại hiệu suất tối ưu và tránh được các lỗi liên quan đến quản lý bộ nhớ.
2. Khai báo và Khởi tạo Mảng
Để khai báo và khởi tạo một mảng trong Rust, bạn cần chỉ định kiểu dữ liệu của các phần tử và kích thước của mảng. Cú pháp cơ bản như sau:
letten_numbers: [i32;10] = [1,2,3,4,5,6,7,8,9,10];
Trong ví dụ trên:
ten_numberslà tên của mảng.[i32; 10]là kiểu dữ liệu của mảng, trong đói32là kiểu của các phần tử (số nguyên 32-bit), và10là kích thước của mảng (có 10 phần tử).[1, 2, ..., 10]là các giá trị khởi tạo cho mảng.
Khởi tạo với giá trị mặc định
Nếu bạn muốn khởi tạo một mảng với tất cả các phần tử có cùng một giá trị, Rust cung cấp một cú pháp ngắn gọn hơn:
letfive_zeros: [i32;5] = [0;5]; // Mảng gồm 5 phần tử, tất cả đều là 0
Cú pháp [giá_trị; kích_thước] sẽ tạo ra một mảng gồm kích_thước phần tử, và tất cả chúng đều có giá_trị đã cho. Điều này rất tiện lợi khi bạn cần một mảng được khởi tạo đồng nhất.
3. Truy cập các Phần tử Mảng
Để truy cập một phần tử cụ thể trong mảng, bạn sử dụng chỉ số (index). Trong Rust (và hầu hết các ngôn ngữ lập trình khác), chỉ số của mảng bắt đầu từ 0. Tức là, phần tử đầu tiên có chỉ số 0, phần tử thứ hai có chỉ số 1, và cứ thế tiếp tục.
letnumbers= [10,20,30,40,50];letfirst_element=numbers[0]; // first_element sẽ là 10letthird_element=numbers[2]; // third_element sẽ là 30println!("Phần tử đầu tiên: {}",first_element);println!("Phần tử thứ ba: {}",third_element);
An toàn khi truy cập mảng trong Rust
Một trong những điểm mạnh của Rust là khả năng đảm bảo an toàn bộ nhớ. Khi bạn truy cập một phần tử của mảng, Rust sẽ kiểm tra xem chỉ số bạn cung cấp có nằm trong phạm vi hợp lệ của mảng hay không:
- Nếu bạn truy cập một chỉ số hợp lệ (ví dụ: từ 0 đến
kích_thước - 1), chương trình sẽ chạy bình thường. - Nếu bạn cố gắng truy cập một chỉ số nằm ngoài phạm vi (ví dụ: chỉ số âm hoặc lớn hơn hoặc bằng kích thước của mảng), Rust sẽ gây ra một lỗi panic tại thời điểm chạy. Điều này là để ngăn chặn các lỗi “truy cập ngoài phạm vi” (out-of-bounds access) nguy hiểm, vốn có thể dẫn đến lỗ hổng bảo mật hoặc hành vi không xác định trong các ngôn ngữ khác.
letdata= [1,2,3];letvalue=data[5]; // Lỗi! Chỉ số 5 nằm ngoài phạm vi mảng có kích thước 3. Chương trình sẽ panic.
Sự kiểm tra này là một phần của cam kết của Rust về an toàn mà không làm giảm hiệu suất, mang lại sự yên tâm cho lập trình viên.
4. Lặp qua Mảng
Để xử lý từng phần tử trong mảng, bạn có thể sử dụng vòng lặp for kết hợp với các phương thức iterator mà Rust cung cấp.
Lặp qua các tham chiếu bất biến (read-only)
Đây là cách phổ biến nhất để đọc các giá trị trong mảng mà không thay đổi chúng:
letnumbers= [10,20,30,40,50];fornuminnumbers.iter() {println!("Giá trị: {}",num); } // Output: // Giá trị: 10 // Giá trị: 20 // ...
Phương thức .iter() trả về một iterator của các tham chiếu bất biến (&T) đến từng phần tử, cho phép bạn đọc giá trị mà không cần chuyển quyền sở hữu.
Lặp qua các tham chiếu khả biến (mutable)
Nếu bạn muốn thay đổi các phần tử trong mảng, bạn cần sử dụng .iter_mut() để lấy các tham chiếu khả biến (&mut T):
letmutscores= [75,80,92];forscoreinscores.iter_mut() { *score+=5; // Cộng thêm 5 điểm cho mỗi phần tử }println!("Điểm mới: {:?}",scores); // Output: Điểm mới: [80, 85, 97]
Lưu ý dấu * trước score để dereference (lấy giá trị mà tham chiếu đang trỏ tới) trước khi sửa đổi.
5. Độ dài Mảng và Kích thước Bộ nhớ
Bạn có thể dễ dàng lấy độ dài (số lượng phần tử) của một mảng bằng phương thức .len():
letnames= ["Alice","Bob","Charlie"];println!("Mảng có {} phần tử.",names.len()); // Output: Mảng có 3 phần tử.
Để biết mảng chiếm bao nhiêu byte trong bộ nhớ, bạn có thể sử dụng hàm std::mem::size_of_val():
letdata: [i32;4] = [1,2,3,4];println!("Mảng chiếm {} bytes.",std::mem::size_of_val(&data)); // Output: Mảng chiếm 16 bytes (vì i32 là 4 bytes, 4 * 4 = 16)
6. Mảng so với Vector: Khi nào sử dụng loại nào?
Mặc dù cả mảng và Vector đều là các cấu trúc dữ liệu lưu trữ một tập hợp các phần tử, nhưng chúng có những khác biệt cơ bản quyết định khi nào nên sử dụng loại nào:
-
Mảng (
[T; N]):- Kích thước cố định: Kích thước phải được biết tại thời điểm biên dịch và không thể thay đổi.
- Lưu trữ trên Stack: Thường được lưu trữ trên stack (ngăn xếp), giúp truy cập nhanh hơn và quản lý bộ nhớ đơn giản hơn.
- Hiệu suất cao: Lý tưởng cho các tập dữ liệu nhỏ, kích thước cố định, nơi hiệu suất là tối quan trọng.
- An toàn: Rust đảm bảo an toàn truy cập mảng tại thời điểm biên dịch hoặc chạy.
-
Vector (
Vec):- Kích thước động: Có thể tăng hoặc giảm kích thước trong quá trình chạy chương trình.
- Lưu trữ trên Heap: Được lưu trữ trên heap (vùng nhớ động), linh hoạt hơn nhưng có thể có chi phí hiệu suất nhỏ hơn so với stack.
- Linh hoạt: Phù hợp khi bạn không biết trước số lượng phần tử hoặc cần thay đổi chúng.
- Cũng an toàn: Rust cũng cung cấp các biện pháp an toàn cho Vector, nhưng việc quản lý bộ nhớ phức tạp hơn một chút.
Lời khuyên: Hãy dùng mảng khi bạn biết chính xác số lượng phần tử và số lượng đó không thay đổi. Còn nếu bạn cần một cấu trúc dữ liệu linh hoạt hơn, có thể thay đổi kích thước, hãy nghĩ đến Vector.
Kết luận
Qua bài viết này, chúng ta đã cùng nhau tìm hiểu về kiểu dữ liệu mảng trong Rust, từ cách khai báo, khởi tạo, truy cập phần tử, đến cách lặp và những điểm khác biệt quan trọng với Vector. Mảng là một công cụ mạnh mẽ và an toàn, là nền tảng vững chắc cho nhiều tác vụ lập trình trong Rust.
Việc nắm vững mảng không chỉ giúp bạn viết code hiệu quả hơn mà còn củng cố hiểu biết về triết lý an toàn và hiệu suất của Rust. Hãy tiếp tục thực hành và thử nghiệm với mảng trong các chương trình Rust của riêng bạn. Chúc bạn thành công trên con đường trở thành một lập trình viên Rust chuyên nghiệp!
Đừng quên theo dõi các bài viết tiếp theo trong series Lập trình Rust cơ bản để khám phá thêm nhiều khía cạnh thú vị khác của ngôn ngữ này nhé!
“`






