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 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 typeCompound 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à: tuplearray

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: breakcontinue ở 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

Add a Comment

Scroll Up