内置类型
Zero 提供了一组小而规整的基本类型。没有奇特的东西,也没有意外:只有每门系统语言都需要的那些类型,命名也保持一致。
| 类别 | 类型 | 说明 |
|---|---|---|
| 有符号整数 | i8、i16、i32、i64 | 补码表示。 |
| 无符号整数 | u8、u16、u32、u64 | 只有 0 和正数。 |
| 指针宽度 | usize、isize | 宽度与平台指针一致。 |
| 浮点数 | f32、f64 | IEEE-754。 |
| 布尔值 | bool | true 或 false。 |
| 字符 | char | 单个 Unicode 标量值。 |
| 字符串 | String | UTF-8 字符串。 |
| 空类型 | Void | “没有有用的值。” |
这就是你日常会用到的全部基本类型。复合类型,比如 shape、枚举 和 choice,都是由它们组合而成的。
整数
整数类型遵循统一的命名规则:i 表示有符号,u 表示无符号,后面跟位宽。所以 i32 是 32 位有符号整数,u8 是无符号字节,i64 是 64 位有符号整数。
let small_signed: i8 = -120
let byte: u8 = 250
let id: i32 = 1
let big: i64 = 9_000_000_000
let index: usize = 0
没有后缀的字面量默认是 i32,除非周围的上下文要求别的类型:
let answer = 42 // i32
需要特定宽度时,可以给字面量加后缀,或者标注绑定的类型:
let byte = 250_u8 // typed literal
let byte: u8 = 250 // typed binding
两种写法得到的值相同。当你直接把字面量传给函数或构造结构体时,字面量后缀的写法很方便:
let pair: BytePair = Pair { left: 1_u8, right: 2_u8 }
该选哪种宽度
一个简单的经验法则:
- 大多数有符号运算用
i32。 它的范围足够覆盖几乎所有需要计数的场景,在各个平台上都很快。 - 字节级操作用
u8。 文件中的字节、缓冲区里的字节、网络上传输的字节。 - 在意范围的非负计数用
u32/u64。 比如超过 2 GB 的文件偏移量、很大的计数。 - 大小和索引用
usize。 它是指针宽度的,与平台寻址内存所用的宽度一致。 - 自纪元以来的时间之类用
i64。 足够以纳秒为单位表示几百年。
选能容纳数据的最小类型是好习惯;但类型选得太小导致溢出,比宽了一位要严重得多。
布尔值
let ok = true
let done: bool = false
bool 只有两个值:true 和 false。它们是字面量,不是需要从别处导入的常量。if 或 while 的条件就是一个 bool:整数和字符串不会被隐式当作真假值。
if ok {
check world.out.write("yes\n")
} else {
check world.out.write("no\n")
}
If/else 会详细介绍条件语句。
浮点数
f32 和 f64 分别是 32 位和 64 位的 IEEE-754 浮点数。需要小数时用它们,比如测量值、比例和几何计算。如果要对货币做精确运算,请用最小单位(分、聪)的整数,而不是浮点数。
let ratio: f32 = 0.5
let pi: f64 = 3.141592653589793
没有后缀的浮点字面量默认是 f64。
字符和字符串
char 保存单个 Unicode 标量值:
let initial: char = 'Z'
String 是字符序列,标准库通常把它编码为 UTF-8。字符串字面量使用双引号:
let message: String = "hello from zero\n"
常见的转义序列都能用:\n 表示换行,\t 表示制表符,\\ 表示反斜杠本身,\" 表示双引号本身。
let multi_line = "line one\nline two\n"
标准库为字符串提供了字节级视图,方便做底层操作。std.mem.span("zero") 会返回一个覆盖这些字节的 Span<u8>,在解析、计算哈希或按字节比较时很有用。
Void
Void 是 Zero 表示“没有有用返回值”的类型。为副作用而存在的函数会使用它:
pub fun main(world: World) -> Void raises {
check world.out.write("hello\n")
}
main 写出一些内容就返回了。没有值需要交回,所以类型是 Void。大多数接触 World 的函数都会用 Void:调用它们是为了效果,而不是结果。
数字字面量中的下划线
较长的数字字面量可以用下划线作视觉分隔。编译器会忽略它们,所以这纯粹是为了可读性:
let big = 9_000_000_000_i64
let bytes = 1_048_576_u32 // 1 MiB
只要数字长到不好数,随便在哪里加都行。
类型化字面量后缀速查表
| 后缀 | 类型 | 示例 |
|---|---|---|
_i8 / _i16 / _i32 / _i64 | 有符号整数 | 127_i8 |
_u8 / _u16 / _u32 / _u64 | 无符号整数 | 255_u8 |
_usize / _isize | 指针宽度 | 0_usize |
_f32 / _f64 | 浮点数 | 0.5_f32 |
当周围的上下文无法确定类型时,构造值就可以用这些后缀。
下一步:函数
光有基本类型,没有处理它们的手段,也派不上用场。下一篇文档介绍 Zero 中的 函数:怎样声明函数、从函数返回值,以及把它们组合起来构建真正的程序。
常见问题
Zero 有哪些基本类型?
Zero 自带固定宽度的有符号整数 i8、i16、i32、i64;无符号整数 u8、u16、u32、u64;指针宽度的整数 usize 和 isize;浮点数 f32 和 f64;bool;char;String;以及用于不返回有用值的函数的 Void。
Zero 默认的整数类型是什么?
像 42 这样没有后缀的整数字面量默认是 i32,除非上下文要求别的类型。要使用特定宽度,可以给字面量加后缀,比如 42_u8 或 42_i64,也可以显式标注绑定的类型:let count: u8 = 42。
Zero 有单独的字符串类型吗?
有。像 "hello" 这样的字符串字面量属于内置的字符串类型,标准库把它当作字节序列(通常是 UTF-8)。如果要做更底层的字节操作,标准库提供了 span 和字节级工具;如果要按字符操作,可以用 char 表示单个标量值。
Zero 中的 Void 是什么意思?
Void 是不产生有用值的函数的返回类型,这类函数只为副作用而存在。约定的 pub fun main(world: World) -> Void raises 签名使用 Void,因为 main 的作用是做 I/O 然后退出,而不是产生一个值。
Zero 中 i32 和 u32 有什么区别?
i32 是 32 位有符号整数,范围是 −2,147,483,648 到 2,147,483,647。u32 是无符号的,范围是 0 到 4,294,967,295。负数有意义时用有符号类型;负数一旦出现就意味着 bug 时,比如计数、索引、大小等,用无符号类型。