Rust World(P2): Các khái niệm cơ bản
Chào mọi người, mình đã quay lại với seri về Rust. Bạn có thể xem phần 1 tại đây
Dưới đây là những nội dung chính của phần 2
- Biến và phạm vi hoạt động – Variable & Scope
- Các kiểu dữ liệu – Data types
- Cấu trúc điều khiển – Control flow
- Hàm – Function
I. Variable & Scope
Variable
Tương tự như trong các ngôn ngữ khác, Rust có 3 kiểu biến: mutable và immutable và constant
1. Immutable variable
– Không thể thay đổi giá trị đã được gán
– Đảm bảo tính an toàn trong môi trường concurrency
let name: &str = "Messi"; let message: String = String::from("Hello Messi"); // name = "Change name"; => Nếu cố tình gán thì compiler sẽ báo lỗi
Mặc dù chúng ta không thể thay đổi giá trị của biến, tuy vậy Rust vẫn cho phép ta tái sử dụng lại biến với kĩ thuật gọi là Shadowing
let shadow_val = 1; // this shadow_val is shadow let shadow_val = 2;
Giá trị đầu tiên là “bóng” của giá trị thứ hai, tức là giá trị thứ hai sẽ là thứ mà compiler nhìn thấy khi sử dụng tên biến
2. Mutable variable
– Có thể thay đổi giá trị sau khi gán
– Cần thêm từ khóa mut
vào phía trước tên biến
let mut age = 1; age = 2;
3. Constant variable
Trong Rust, dạng khai báo thông dụng nhất là: sử dụng từ khóa const
const VN_CAPITAL: &str = "Ha Noi";
Có 1 chút khác biệt giữa const
và let
– Khi sử dụng const
thì kiểu dữ liệu cần phải khai báo tường minh. Trong khi đó, với let
thì không bắt buộc, kiểu của dữ liệu sẽ được nội suy trong quá trình compile
– const
có thể được khai báo ở bất kì phạm vi, bao gồm cả global
, với let
thì bị giới hạn phạm vi (scope)
– const
có thể tồn tại trong suốt chương trình còn let
sẽ bị hủy khi ra khỏi phạm vi giới hạn (scope)
Scope
Biến trong Rust có phạm vi sử dụng (scope) và bị giới hạn thời gian sống (lifetime) trong phạm vi của một block
Một block được khai báo bằng cách sử dụng cặp dấu: {}
let x1 = 1; // Phạm vi ở main function let mut y = 1; { // Đây là 1 block và nó có phạm vi nhỏ hơn main function // Giá trị x1 & x2 chỉ tồn tại trong block này let x1 = 2; let x2 = 2; let y1 = y; }
Biến y
(mutable) được gán cho biến y1
(immutable) bên trong block. Không thể thay đổi giá trị của y
nếu y1
còn sống. Điều này gọi là sự đóng băng (Freezing).
Hiển đơn giản là không thể chỉ định giá trị khác cho biến y
nếu phạm vi block chưa kết thúc.
Thời gian sống của biến sẽ được mình trình bày ở bài khác.
II. Data Types
Rust là một ngôn ngữ kiểu tĩnh vì thế mọi giá trị trong Rust phải có 1 type xác định. Kiểu dữ liệu có thể được khai báo tường minh hoặc ngầm định (compiler sẽ tự nội suy)
Các kiểu dữ liệu nguyên thủy trong Rust gồm có: Scalar type
và Compound type
1. Scalar Type
Có 4 scalar type chính gồm có: integers, floating-point numbers, booleans and characters
Integer type bao gồm 2 dạng: Signed(`i`) & Unsigned(`u`)
i8 - u8 - length: 8 bit i16 - u16 - length: 16 bit i32 - u32 - length: 32 bit i64 - u64 - length: 64 bit i128 - u128 - length: 128 bit isize - usize - length: depend on the architecture of the computer: x64 ~> 64 bit and x86/x32 ~> 32 bit
Float-point có 2 dạng là: f32 & f64. Mặc định là f64
let fl_val = 1.0; let fl_val: f32 = 1.0;
Boolean có 2 giá trị: true hoặc false. Nó chỉ có kích thước là 1 byte
let b_val = true; let b_val: bool = false;
Character được khai báo với nháy đơn, nó sẽ chiếm kích thước là 4 bytes
let c = 'z'; let c: char = 'z'; let c = '😻';
Sử dụng từ khóa as
để chuyển đổi kiểu dữ liệu (type-casting)
let decimal = 65.4321_f32; let idx: u32 = decimal as u32;
2. Compound type
Có thể nhóm nhiều giá trị thành 1 type duy nhất. Rust có 2 kiểu compound type chính là: tuple
và array
Tuple type:
– Có kích thước cố định và không thể thay đổi
let tuple_val = (1, 2, 3); let tuple_val: (u8, u8, char) = (1, 2, 'z'); let (v1, v2, v3) = tuple_val; // destructure a tuple value let first_element = tuple_val.0; // access the first element in tuple
Array:
let arr = [1, 2, 3, 4, 5]; // khai báo array có 5 phần tử let arr: [u8; 2] = [1, 2]; // khai báo array có 2 phần tử với kiểu dữ liệu u8 let first_element = arr[0]; // truy cập phần tử đầu tiên
III. Control flow
Điểm qua nhanh 1 số control flow trong Rust
1. If
let n = 1; if n == 1 { println!("Value equals to 1") } else if n > 1 { println!("Value is greater than 1") } else { println!("Value is less than 1") } // `if` returns value let r = if n == 1 { 6 } else { 0 };
2. Loop
let mut i: u8 = 0; let val = loop { // Return value với loop i += 1; println!("Using loop kind: {:?}", i); if i >= 3 { break 10; } };
while i < 5 { i += 1; }
for item in arr { println!("Element in arr: {}", item); } for i in 0..=1 { println!("Element in arr: {}", arr[i]); } for i in 0..2 { println!("Element in arr: {}", arr[i]); }
Có thể sử dụng: break
và continue
ở trong vòng lặp
– break: thoát khỏi vòng lặp hiện tại
– continue: bắt đầu 1 vòng lặp mới
Có thể đặt label
cho loop, while, for
'lab: loop { break 'lab; } 'lab: while 1 == 1 { break 'lab; } 'lab: for item in arr { continue 'lab; }
IV. Function
Hàm trong Rust được tạo bởi từ khóa: fn
fn add_total(x1: u8, x2: u8) -> u8 { // return type is u8 by using `->` x1 + x2 // or return x1 + x2 } // The last line without `;` will implicitly return println!("Total 1 + 2 = {:?}", add_total(1, 2)); fn show() { println!("Hello World"); } // same: fn show() -> () { println!("Hello World"); } show();
Trên đây là những ví dụ cơ bản với hàm, các kiến thức nâng cao hơn sẽ được trình bày xen kẽ trong các bài viết sau
Hẹn gặp lại các bạn trong bài viết tiếp theo về: Struct, Enum và String trong Rust