Menu

Kiểu nguyên thủy Zero: số nguyên, số thực, Bool, String, Void

Các kiểu có sẵn trong Zero: số nguyên có dấu và không dấu với mọi độ rộng, số thực, boolean, ký tự, chuỗi và kiểu rỗng Void.

Các kiểu có sẵn

Zero cung cấp một tập kiểu nguyên thủy nhỏ và nhất quán. Không có gì lạ, không có gì bất ngờ: chỉ những kiểu mà mọi ngôn ngữ lập trình hệ thống đều cần, được đặt tên thống nhất.

NhómKiểuGhi chú
Số nguyên có dấui8, i16, i32, i64Bù hai (two's complement).
Số nguyên không dấuu8, u16, u32, u64Chỉ 0 và số dương.
Theo kích thước con trỏusize, isizeĐộ rộng bằng con trỏ của nền tảng.
Số thựcf32, f64IEEE-754.
Booleanbooltrue hoặc false.
Ký tựcharMột giá trị scalar Unicode.
ChuỗiStringChuỗi UTF-8.
RỗngVoid"Không có giá trị hữu ích."

Đó là toàn bộ các kiểu nguyên thủy bạn sẽ dùng hằng ngày. Các kiểu phức hợp như shape, enum, choice đều được xây dựng từ những kiểu này.

Số nguyên

Các kiểu số nguyên đặt tên theo một quy tắc thống nhất: i cho có dấu, u cho không dấu, theo sau là số bit. Vậy i32 là số nguyên có dấu 32 bit; u8 là một byte không dấu; i64 là số nguyên có dấu 64 bit.

let small_signed: i8   = -120
let byte:         u8   = 250
let id:           i32  = 1
let big:          i64  = 9_000_000_000
let index:        usize = 0

Kiểu mặc định cho một literal không có hậu tố là i32, trừ khi ngữ cảnh xung quanh buộc phải dùng kiểu khác:

let answer = 42       // i32

Khi cần một độ rộng cụ thể, hãy gắn hậu tố vào literal hoặc chú thích kiểu cho binding:

let byte = 250_u8     // typed literal
let byte: u8 = 250    // typed binding

Cả hai dạng cho ra cùng một giá trị. Dạng hậu tố trên literal rất tiện khi bạn truyền literal thẳng vào một hàm hoặc khi tạo một struct:

let pair: BytePair = Pair { left: 1_u8, right: 2_u8 }

Khi nào chọn độ rộng nào

Một quy tắc ngắn gọn:

  • i32 cho hầu hết phép toán có dấu. Đủ rộng cho gần như mọi thứ bạn cần đếm, chạy nhanh trên mọi nền tảng.
  • u8 cho công việc ở mức byte. Byte đọc từ tệp, byte trong bộ đệm, byte truyền qua mạng.
  • u32 / u64 cho số đếm không âm khi phạm vi quan trọng. Vị trí trong tệp vượt quá 2 GB, các số đếm lớn.
  • usize cho kích thước và chỉ số. Theo kích thước con trỏ, khớp với cách nền tảng đánh địa chỉ bộ nhớ.
  • i64 cho thời gian tính từ epoch và những thứ tương tự. Đủ lớn để đếm nano giây trong hàng trăm năm.

Chọn kiểu nhỏ nhất vừa đủ là thói quen tốt; nhưng chọn kiểu quá nhỏ rồi bị tràn số là vấn đề lớn hơn nhiều so với chọn dư một bit.

Boolean

let ok = true
let done: bool = false

bool có đúng hai giá trị: true và false. Chúng là literal, không phải hằng số bạn phải import từ đâu đó. Điều kiện trong if hay while là một bool: số nguyên hay chuỗi không được ngầm hiểu là đúng hay sai.

if ok {
    check world.out.write("yes\n")
} else {
    check world.out.write("no\n")
}

Bài If/else trình bày chi tiết về câu lệnh điều kiện.

Số thực

f32 và f64 lần lượt là số thực dấu phẩy động IEEE-754 32 bit và 64 bit. Dùng chúng khi bạn cần giá trị có phần thập phân: số đo, tỷ lệ, hình học. Với phép tính tiền tệ cần chính xác tuyệt đối, hãy ưu tiên số nguyên theo đơn vị nhỏ nhất (cent, satoshi) thay vì số thực.

let ratio: f32 = 0.5
let pi:    f64 = 3.141592653589793

f64 là kiểu mặc định cho literal số thực không có hậu tố.

Ký tự và chuỗi

Một char chứa một giá trị scalar Unicode duy nhất:

let initial: char = 'Z'

Một String là một dãy ký tự, thường được thư viện chuẩn mã hóa theo UTF-8. Literal chuỗi dùng dấu ngoặc kép:

let message: String = "hello from zero\n"

Các chuỗi thoát (escape sequence) quen thuộc đều dùng được: \n để xuống dòng, \t cho tab, \\ cho một dấu gạch chéo ngược, \" cho một dấu ngoặc kép.

let multi_line = "line one\nline two\n"

Thư viện chuẩn cung cấp các góc nhìn theo byte trên một chuỗi cho công việc cấp thấp. Dạng std.mem.span("zero") trả về một Span<u8> trên các byte, rất hữu ích khi bạn phân tích cú pháp, băm hoặc so sánh theo từng byte.

Void

Void là kiểu "không có giá trị trả về hữu ích" của Zero. Các hàm tồn tại vì hiệu ứng phụ của chúng dùng kiểu này:

pub fun main(world: World) -> Void raises {
    check world.out.write("hello\n")
}

main ghi ra một thứ rồi trả về. Không có giá trị nào để trả lại, nên kiểu là Void. Bạn sẽ thấy Void trên hầu hết các hàm làm việc với World: người ta gọi chúng vì hiệu ứng, không phải vì kết quả.

Dấu gạch dưới trong literal số

Các literal số dài có thể dùng dấu gạch dưới làm dấu phân cách cho dễ nhìn. Trình biên dịch bỏ qua chúng, nên đây thuần túy là tính năng giúp dễ đọc:

let big   = 9_000_000_000_i64
let bytes = 1_048_576_u32     // 1 MiB

Hãy thêm chúng vào bất cứ chỗ nào các chữ số bắt đầu khó đếm.

Cheat sheet hậu tố literal có kiểu

Hậu tốKiểuVí dụ
_i8 / _i16 / _i32 / _i64Số nguyên có dấu127_i8
_u8 / _u16 / _u32 / _u64Số nguyên không dấu255_u8
_usize / _isizeTheo kích thước con trỏ0_usize
_f32 / _f64Số thực0.5_f32

Hãy dùng chúng khi bạn tạo một giá trị mà ngữ cảnh xung quanh không xác định được kiểu.

Tiếp theo: hàm

Kiểu nguyên thủy chẳng có ích gì nếu không có gì để làm với chúng. Tài liệu tiếp theo trình bày về hàm trong Zero: cách khai báo, cách trả về giá trị và cách kết hợp chúng để xây dựng chương trình thực tế.

Câu hỏi thường gặp

Zero có những kiểu nguyên thủy nào?

Zero có sẵn các số nguyên có dấu theo kích thước i8, i16, i32, i64; số nguyên không dấu u8, u16, u32, u64; số nguyên theo kích thước con trỏ usize và isize; số thực f32 và f64; bool; char; String; và Void cho các hàm không trả về giá trị hữu ích nào.

Kiểu số nguyên mặc định trong Zero là gì?

Một literal số nguyên không có hậu tố như 42 mặc định là i32, trừ khi ngữ cảnh buộc phải dùng kiểu khác. Để dùng một độ rộng cụ thể, hãy viết literal kèm hậu tố như 42_u8 hoặc 42_i64, hoặc chú thích kiểu tường minh cho binding bằng let count: u8 = 42.

Zero có kiểu chuỗi riêng không?

Có. Các literal chuỗi như "hello" có một kiểu chuỗi dựng sẵn, được thư viện chuẩn xem như một dãy byte (thường là UTF-8). Để làm việc ở mức byte, thư viện chuẩn cung cấp span và các tiện ích theo byte; còn với thao tác theo ký tự thì có char cho từng giá trị scalar riêng lẻ.

Void trong Zero có nghĩa là gì?

Void là kiểu trả về của một hàm không tạo ra giá trị hữu ích nào, hàm đó chỉ tồn tại vì các hiệu ứng phụ (side effect) của nó. Chữ ký quen thuộc pub fun main(world: World) -> Void raises dùng Void vì main có nhiệm vụ thực hiện I/O rồi kết thúc, không phải tạo ra một giá trị.

i32 và u32 trong Zero khác nhau thế nào?

i32 là số nguyên có dấu 32 bit, phạm vi từ −2.147.483.648 đến 2.147.483.647. u32 là số không dấu, phạm vi từ 0 đến 4.294.967.295. Dùng kiểu có dấu khi giá trị âm có ý nghĩa, và kiểu không dấu khi giá trị âm sẽ là bug, chẳng hạn với số đếm, chỉ số, kích thước, v.v.

Hình minh họa các ngôn ngữ lập trình của Coddy

Học lập trình cùng Coddy

Bắt đầu