Bỏ qua để đến nội dung

Kiểu dữ liệu

Mọi biến đều phải mang kiểu khai báo. Không suy luận kiểu ẩn, không đổi kiểu ẩn, không che bóng tên, và điều kiện phải là bool thật. Một người kiểm toán đọc từng dòng và biết chính xác điều gì xảy ra — đó không phải phong cách lập trình, đó là một yêu cầu kiểm toán.

Kiểu Ý nghĩa Biểu diễn C
int Số nguyên có dấu 64-bit, kiểm tra tràn int64_t
float IEEE 754 binary64 double
bool true / false bool
str Chuỗi byte, độ dài tường minh hl_str*
list[T] Mảng động hl_list*
map[str, T] Bảng băm, khoá str hl_map*
tainted[T] Bọc taint — thuộc tính lúc biên dịch biểu diễn như T
Cap[E] Capability token cho effect E —
void Chỉ dùng làm kiểu trả về —
Name, Name[T…] Struct hoặc enum do người dùng định nghĩa Name*

int → float cần x.to_float(); chiều ngược lại cần x.to_int().

Từ Đến Cách
int float x.to_float() — chính xác
float int x.to_int() — cắt cụt về 0
bất kỳ str x.to_str()
str int int_parse(s) trả Result[int, str]
list[T] list[U] nhóm list_convert_* của std.list

int → float là chính xác vì int là 64-bit còn float có mantissa 64-bit. float → int thì cắt cụt, và ngôn ngữ không giấu việc đó.

struct Point {
x: int,
y: int
}
enum Shape {
Circle(float),
Rect(int, int)
}
fn area(s: Shape) -> float {
return match s {
Shape.Circle(r) => 3.14159 * r * r,
Shape.Rect(w, h) => (w * h).to_float()
}
}

match là một biểu thức và phải phủ hết các biến thể. Đây là nơi “không null” được thực thi ở mức kiểu: không có null để kiểm tra, nên Option[T] và Result[T, E] là hai cách duy nhất để biểu diễn “có thể không có”.

Kiểu generic được đơn khoá hoá: mỗi instantiation sinh ra mã riêng, không boxing, không dispatch ảo — hiệu năng bằng viết tay.

enum Option[T] { Some(T), None }
enum Result[T, E] { Ok(T), Err(E) }
fn unwrap_or(r: Result[int, str], fallback: int) -> int {
return match r {
Result.Ok(v) => v,
Result.Err(e) => fallback
}
}

clone() tạo một bản sao độc lập; take(x) chuyển quyền sở hữu và đánh dấu binding gốc là moved; drop(x) giải phóng tường minh.

let xs: list[int] = [10, 20, 30]
let ys: list[int] = clone(xs)
ys.push(40) # chỉ ys thay đổi
# xs.len() == 3, ys.len() == 4

Dùng một giá trị đã move là lỗi biên dịch (use of moved value: s), nên không có double free và không có use-after-free phải kiểm toán lúc chạy.

fn safe_div(a: int, b: int) -> Result[int, str] {
if b == 0 {
return Result.Err("division by zero")
}
return Result.Ok(a / b)
}
fn chained_div(a: int, b: int, c: int) -> Result[int, str] {
let q: int = safe_div(a, b)? # `?` trả về sớm khi gặp Err
let r: int = safe_div(q, c)?
return Result.Ok(r)
}

panic không dành cho lỗi dự kiến — chỉ dành cho bug lập trình. Lỗi dự kiến (tệp không có, parse hỏng, input sai) phải đi qua Result.