Trong phần này chúng ta tìm hiểu về 3 kiểu dữ liệu Array, Vector và Slice trong Rust.
Array
Array (hay có tên khác là mảng) là kiểu dữ liệu lưu trữ theo dạng danh sách, tức là giá trị sẽ là một dãy các giá trị chứ không còn là các giá trị đơn độc nữa. Ví dụ:
fn main() {
let langs = ["Rust", "C++", "Java", "Python"];
println!("{:?}", langs);
}
Để tạo một array thì chúng ta mở một cặp dấu ngoặc vuông []
, bên trong chúng ta ghi danh sách phần tử bằng các giá trị và cách nhau bởi dấu phẩy. Các phần tử có thể có kiểu dữ liệu nào cũng được, chẳng hạn như i32
, f32
…v.v nhưng các phần tử phải có kiểu dữ liệu giống nhau. Khi đã khởi tạo một array thì kích thước của array này không thể thay đổi được nữa.
Mặc định thì các phần tử bên trong cũng không thể thay đổi được giá trị, nếu muốn thay đổi giá trị các phần tử thì chúng ta khai báo array với từ khóa mut
, tuy nhiên số lượng phần tử của array thì vẫn luôn không đổi.
Khi tạo mảng chúng ta có thể quy định số phần tử cho array thông qua cú pháp như sau:
fn main() {
let langs: [&str; 4] = ["Rust", "C++", "Java", "Python"];
println!("{:?}", langs);
}
Phía sau tên array là một cặp dấu ngoặc vuông, bên trong là tên kiểu dữ liệu và số lượng phần tử. Ở trên chúng ta khai báo array có kiểu dữ liệu &str
và số lượng phần tử là 4.
Chúng ta cũng có thể tạo một mảng với tất cả các phần tử có cùng chung giá trị, ví dụ:
fn main() {
let names = ["PhoCode"; 3];
println!("{:?}", names); // ["PhoCode", "PhoCode", "PhoCode"]
}
Để tạo một mảng rỗng thì chúng ta không đưa giá trị vào mảng ví dụ:
fn main() {
let arr: [i32; 0] = [];
println!("{:?}", arr);
}
Chúng ta có thể lấy số lượng phần tử của mảng thông qua hàm len()
như sau:
fn main() {
let arr: [i32; 3] = [23, 856, 9302];
println!("arr has {} elements", arr.len());
}
Để truy xuất vào các phần tử của mảng thì chúng ta sử dụng toán tử []
và truyền vào số thứ tự của phần tử, ví dụ:
fn main() {
let arr: [i32; 3] = [23, 856, 9302];
println!("1st element in arr is {}", arr[0]);
println!("2nd element in arr is {}", arr[1]);
println!("3rd element in arr is {}", arr[2]);
}
Số thứ tự của các phần tử trong mảng luôn luôn bắt đầu từ 0. Và do đó phần tử cuối cùng trong mảng luôn luôn có số thứ tự là len() - 1
. Nếu chỉ số mảng vượt ra ngoài phạm vi cho phép của mảng thì Rust sẽ báo lỗi.
Nếu chúng ta cố gắng truy xuất phần tử nằm ngoài phạm vi mảng thì Rust sẽ in một câu cảnh báo khi biên dịch.
fn main() {
let arr: [i32; 3] = [23, 856, 9302];
println!("5th element is {}", arr[5]);
}
warning: this expression will panic at run-time println!("5th element is {}", arr[5]); ^^^^^^ index out of bounds: the len is 3 but the index is 5
Ngoài ra khi chạy thì Rust sẽ báo lỗi runtime, chúng ta sẽ tìm hiểu về lỗi runtime sau.
thread 'main' panicked at 'index out of bounds: the len is 3 but the index is 5'
Chúng ta có thể sử dụng câu lệnh for
để lặp qua từng phần tử trong mảng như sau:
fn main() {
let arr: [i32; 3] = [23, 856, 9302];
for i in 0..arr.len() {
println!("{}", arr[i]);
}
}
23 856 9302
Với kiểu lặp ở trên thì chúng ta sử dụng số thứ tự của mỗi phần tử trong mỗi lần lặp, và mỗi lần lặp như vậy Rust cũng sẽ kiểm tra xem số thứ tự có nằm trong phạm vi cho phép của mảng hay không. Tuy nhiên chúng ta có thể lặp theo kiểu iteration sau đây:
fn main() {
let arr: [i32; 3] = [23, 856, 9302];
for element in arr.iter() {
println!("{}", element);
}
}
Vòng lặp theo kiểu trên hiệu quả hơn so với lặp theo chỉ số. Chúng ta sẽ tìm hiểu về iteration sau.
Vector
Kiểu dữ liệu array ở trên có một nhược điểm là kích thước không thể thay đổi được, kiểu vector ra đời là để thay thế tính chất trên.
Tương tự với array thì vector cũng lưu trữ danh sách các phần tử, các phần thử có thể có kiểu dữ liệu gì cũng được nhưng phải giống nhau.
Để tạo một vector thì chúng ta dùng hàm new()
hoặc macro vec!
, ví dụ:
fn main() {
let vec_number: Vec<i32> = Vec::new();
let vec_str = vec!["Lorem", "Ipsum"];
}
Khi dùng hàm new()
thì chúng ta tạo khai báo một biến vector và phải chỉ rõ kiểu dữ liệu trong vector theo cú pháp Vec<tên_kiểu>
. Còn khi chúng ta khai báo và khởi tạo vector theo cú pháp vec!
thì chúng ta không cần khai báo kiểu, mà thay vào đó chúng ta cung cấp các phần tử cho vector và vector sẽ tự động suy ra kiểu dựa theo các giá trị mà chúng ta truyền vào.
Chúng ta cũng có thể dùng hàm with_capacity()
để tạo vector với số lượng định sẵn:
fn main() {
let numbers : Vec<i32> = Vec::with_capacity(10);
}
Trong đoạn code trên biến numbers
sẽ được tạo ra với 10 phần tử rỗng.
Việc lặp qua các phần tử trong vector cũng giống như trong array:
fn main() {
let numbers = vec![423, 943, 192];
for element in numbers {
println!("{}", element);
}
}
423 943 192
Chúng ta cũng có thể thêm một phần tử vào vector bằng hàm push()
, xóa một phần tử ra khỏi vector với hàm pop()
:
fn main() {
let mut numbers = vec![423, 943, 192];
numbers.push(13);
numbers.push(52);
println!("{:?}", numbers); // [423, 943, 192, 13, 52]
numbers.pop();
println!("{:?}", numbers); // [423, 943, 192, 13]
}
Hàm push()
nhận vào giá trị có kiểu dữ liệu giống với kiểu của vector, và tạo ra phần tử mới rồi đưa vào cuối vector, hàm pop()
sẽ xóa phần tử cuối cùng trong vector. Lưu ý biến phải được khai báo mut
.
Slice
Slice là một cơ chế cho phép chúng ta lấy mảng con từ một mảng cha. Cú pháp như sau:
&<tên_mảng>[m..n]
Cú pháp trên sẽ tạo ra một mảng con từ mảng cha với các phần tử từ m đến n – 1
VÍ dụ:
fn main() {
let numbers = vec![423, 943, 192, 13, 52];
let sub_numbers = &numbers[1..3];
println!("{:?}", sub_numbers);
}
Trong đoạn code trên chúng ta có mảng numbers
có 5 phần tử, sau đó chúng ta tạo ra mảng con sub_numbers
lấy bằng phần tử thứ 1đến phần tử thứ 2.
[943, 192]