整数类型
整数类型分为有符号(signed)整数类型和无符号(unsigned)整数类型。
有符号整数类型包括 Int8、Int16、Int32、Int64 和 IntNative,分别用于表示编码长度为 8-bit、16-bit、32-bit、64-bit 和平台相关大小的有符号整数值的类型。
无符号整数类型包括 UInt8、UInt16、UInt32、UInt64 和 UIntNative,分别用于表示编码长度为 8-bit、16-bit、32-bit、64-bit 和平台相关大小的无符号整数值的类型。
对于编码长度为 N 的有符号整数类型,其表示范围为:;对于编码长度为 N 的无符号整数类型,其表示范围为:。下表列出了所有整数类型的表示范围:
类型 | 表示范围 |
---|---|
Int8 | |
Int16 | |
Int32 | |
Int64 | |
IntNative | platform dependent |
UInt8 | |
UInt16 | |
UInt32 | |
UInt64 | |
UIntNative | platform dependent |
程序具体使用哪种整数类型,取决于该程序中需要处理的整数的性质和范围。在 Int64 类型适合的情况下,首选 Int64 类型,因为 Int64 的表示范围足够大,并且整数类型字面量在没有类型上下文的情况下默认推断为 Int64 类型,可以避免不必要的类型转换。
整数类型字面量
整数类型字面量有 4 种进制表示形式:二进制(使用 0b 或 0B 前缀)、八进制(使用 0o 或 0O 前缀)、十进制(没有前缀)、十六进制(使用 0x 或 0X 前缀)。例如,对于十进制数 24,表示成二进制是 0b00011000(或 0B00011000),表示成八进制是 0o30(或 0O30),表示成十六进制是 0x18(或 0X18)。
在各进制表示中,可以使用下划线 _ 充当分隔符的作用,方便识别数值的位数,如 0b0001_1000。
对于整数类型字面量,如果它的值超出了上下文要求的整数类型的表示范围,编译器将会报错。
let x: Int8 = 128 // Error, 128 out of the range of Int8
let y: UInt8 = 256 // Error, 256 out of the range of UInt8
let z: Int32 = 0x8000_0000 // Error, 0x8000_0000 out of the range of Int32
在使用整数类型字面量时,可以通过加入后缀来明确整数字面量的类型,后缀与类型的对应为:
后缀 | 类型 | 后缀 | 类型 |
---|---|---|---|
i8 | Int8 | u8 | UInt8 |
i16 | Int16 | u16 | UInt16 |
i32 | Int32 | u32 | UInt32 |
i64 | Int64 | u64 | UInt64 |
加入了后缀的整数字面量可以像下面的方式来使用:
var x = 100i8 // x is 100 with type Int8
var y = 0x10u64 // y is 16 with type UInt64
var z = 0o432i32 // z is 282 with type Int32
整数类型支持的操作
整数类型默认支持的操作符包括:算术操作符、位操作符、关系操作符、自增和自减操作符、赋值操作符、复合赋值操作符。各操作符的优先级参见附录中的操作符。
-
算术操作符包括:一元负号(-)、加法(+)、减法(-)、乘法(*)、除法(/)、取模(%)、幂运算(**)。
- 除了一元负号(-)和幂运算(**),其他操作符要求左右操作数是相同的类型。
- *,/,+ 和 - 的操作数可以是整数类型或浮点类型。
- % 的操作数只支持整数类型。
- ** 的左操作数只能为 Int64 类型或 Float64 类型,并且:
- 当左操作数类型为 Int64 时,右操作数只能为 UInt64 类型,表达式的类型为 Int64。
- 当左操作数类型为 Float64 时,右操作数只能为 Int64 类型或 Float64 类型,表达式的类型为 Float64。
幂运算的使用,见如下示例:
let p1 = 2 ** 3 // p1 = 8 let p2 = 2 ** UInt64(3 ** 2) // p2 = 512 let p3 = 2.0 ** 3.0 // p3 = 8.0 let p4 = 2.0 ** 3 ** 2 // p4 = 512.0 let p5 = 2.0 ** 3.0 // p5 = 8.0 let p6 = 2.0 ** 3.0 ** 2.0 // p6 = 512.0
-
位操作符包括:按位求反(!)、左移(<<)、右移(>>)、按位与(&)、按位异或(^)、按位或(|)。注意,按位与、按位异或和按位或操作符要求左右操作数是相同的整数类型。
-
关系操作符包括:小于(<)、大于(>)、小于等于(<=)、大于等于(>=)、相等(==)、不等(!=)。要求关系操作符的左右操作数是相同的整数类型。
-
自增和自减操作符包括:自增(++)和自减(–)。注意,仓颉中的自增和自减操作符只能作为一元后缀操作符使用。
-
赋值操作符即 =,复合赋值操作符包括:+=、-=、*=、/=、%=、**=、<<=、>>=、&=、^=、|=。
注意
本章中我们所提及的某个类型支持的操作,均是指在没有操作符重载的前提下。
浮点类型
浮点类型包括 Float16、 Float32 和 Float64,分别用于表示编码长度为 16-bit、 32-bit 和 64-bit 的浮点数(带小数部分的数字,如 3.14159、8.24 和 0.1 等)的类型。Float16、 Float32 和 Float64 分别对应 IEEE 754 中的半精度格式(即 binary16)、单精度格式(即 binary32)和双精度格式(即 binary64)。
Float64 的精度约为小数点后 15 位,Float32 的精度约为小数点后 6 位,Float16 的精度约为小数点后 3 位。使用哪种浮点类型,取决于代码中需要处理的浮点数的性质和范围。在多种浮点类型都适合的情况下,首选精度高的浮点类型,因为精度低的浮点类型的累计计算误差很容易扩散,并且它能精确表示的整数范围也很有限。
浮点类型字面量
浮点类型字面量有两种进制表示形式:十进制、十六进制。在十进制表示中,一个浮点字面量至少要包含一个整数部分或一个小数部分,没有小数部分时必须包含指数部分(以 e 或 E 为前缀,底数为 10)。在十六进制表示中,一个浮点字面量除了至少要包含一个整数部分或小数部分(以 0x 或 0X 为前缀),同时必须包含指数部分(以 p 或 P 为前缀,底数为 2)。
下面的例子展示了浮点字面量的使用:
let a: Float32 = 3.14let b: Float32 = 2e3let c: Float32 = 2.4e-1let d: Float64 = .123e2let e: Float64 = 0x1.1p0let f: Float64 = 0x1p2let g: Float64 = 0x.2p4
在使用十进制浮点数字面量时,可以通过加入后缀来明确浮点数字面量的类型,后缀与类型的对应为:
后缀 | 类型 |
---|---|
f16 | Float16 |
f32 | Float32 |
f64 | Float64 |
加入了后缀的浮点数字面量可以像下面的方式来使用:
let a = 3.14f32 // a is 3.14 with type Float32
let b = 2e3f32 // b is 2e3 with type Float32
let c = 2.4e-1f64 // c is 2.4e-1 with type Float64
let d = .123e2f64 // d is .123e2 with type Float64
浮点类型支持的操作
浮点类型默认支持的操作符包括:算术操作符、关系操作符、赋值操作符、复合赋值操作符。浮点类型不支持自增和自减操作符。
布尔类型
布尔类型使用 Bool 表示,用来表示逻辑中的真和假。
布尔类型字面量
布尔类型只有两个字面量:true 和 false。
下面的例子展示了布尔字面量的使用:
let a: Bool = true
let b: Bool = false
布尔类型支持的操作
布尔类型支持的操作符包括:逻辑操作符(逻辑非 !,逻辑与 &&,逻辑或 ||)、部分关系操作符(== 和 !=)、赋值操作符、部分复合赋值操作符(&&= 和 ||=)。
字符类型
字符类型使用 Rune 表示,可以表示 Unicode 字符集中的所有字符。
字符类型字面量
字符类型字面量有三种形式:单个字符、转义字符和通用字符。一个 Rune 字面量由字符 r 开头,后跟一个由一对单引号或双引号包含的字符。
单个字符的字符字面量举例:
let a: Rune = r'a'
let b: Rune = r"b"
转义字符是指在一个字符序列中对后面的字符进行另一种解释的字符。转义字符使用转义符号 \ 开头,后面加需要转义的字符。举例如下:
let slash: Rune = '\\'
let newLine: Rune = '\n'
let tab: Rune = '\t'
通用字符以 \u 开头,后面加上定义在一对花括号中的 1~8 个十六进制数,即可表示对应的 Unicode 值代表的字符。举例如下:
main() {
let he: Rune = '\u{4f60}'
let llo: Rune = '\u{597d}'
print(he)
print(llo)
}
编译并执行上述代码,输出结果为:
你好
字符类型支持的操作
字符类型仅支持关系操作符:小于(<)、大于(>)、小于等于(<=)、大于等于(>=)、相等(==)、不等(!=)。比较的是字符的 Unicode 值。
字符串类型
字符串类型使用 String 表示,用于表达文本数据,由一串 Unicode 字符组合而成。
字符串字面量
字符串字面量分为三类:单行字符串字面量,多行字符串字面量,多行原始字符串字面量。
单行字符串字面量的内容定义在一对单引号或一对双引号之内,引号中的内容可以是任意数量的(除了非转义的双引号和单独出现的 \ 之外的)任意字符。单行字符串字面量只能写在同一行,不能跨越多行。举例如下:
let s1: String = ""
let s2 = 'Hello Cangjie Lang'
let s3 = "\"Hello Cangjie Lang\""
let s4 = 'Hello Cangjie Lang\n'
多行字符串字面量开头结尾需各存在三个双引号(“”")或三个单引号(‘’')。字面量的内容从开头的三个引号换行后的第一行开始,到结尾的三个引号之前为止,之间的内容可以是任意数量的(除单独出现的 \ 之外的)任意字符。不同于单行字符串字面量,多行字符串字面量可以跨越多行。举例如下:
let s1: String = """
"""
let s2 = '''
Hello,
Cangjie Lang'''
多行原始字符串字面量以一个或多个井号(#)和一个单引号(')或双引号(")开头,后跟任意数量的合法字符,直到出现与字符串开头相同的引号和与字符串开头相同数量的井号为止。在当前文件结束之前,如果还没遇到匹配的双引号和相同个数的井号,则编译报错。与多行字符串字面量一样,原始多行字符串字面量可以跨越多行。不同支持在于,转义规则不适用于多行原始字符串字面量,字面量中的内容会维持原样(转义字符不会被转义,如下例中 s2 中的 \n 不是换行符,而是由 \ 和 n 组成的字符串 \n)。举例如下:
let s1: String = #""#
let s2 = ##'\n'##
let s3 = ###"
Hello,
Cangjie
Lang"###
对于形如 left = right 的赋值操作,如果左操作数的类型是 Byte,并且右操作数是一个表示 ASCII 字符的字符串字面量,那么右操作数的字符串将分别被强制转换为 Byte 类型,再进行赋值;如果左操作数的类型是 Rune,并且右操作数是一个单字符的字符串字面量,那么右操作数的字符串将分别被强制转换为 Rune 类型,再进行赋值。
main() {
var b: Byte = "0"
print(b)
b = "1"
print(b)
var r: Rune = "0"
print(r)
r = "1"
print(r)
}
编译并执行上述代码,输出结果为:
484901
插值字符串
插值字符串是一种包含一个或多个插值表达式的字符串字面量(不适用于多行原始字符串字面量),通过将表达式插入到字符串中,可以有效避免字符串拼接的问题。虽然我们直到现在才介绍它,但其实它早已经出现在之前的示例代码中,因为我们经常在 println 函数中输出非字符串类型的变量值,例如 println(“${x}”)。
插值表达式必须用花括号 {} 包起来,并在 {} 之前加上 $ 前缀。{} 中可以包含一个或者多个声明或表达式。
当插值字符串求值时,每个插值表达式所在位置会被 {} 中的最后一项的值替换,整个插值字符串最终仍是一个字符串。
下面是插值字符串的简单示例:
main() {
let fruit = "apples"
let count = 10
let s = "There are ${count * count} ${fruit}"
println(s)
let r = 2.4
let area = "The area of a circle with radius ${r} is ${let PI = 3.141592; PI * r * r}"
println(area)
}
编译并执行上述代码,输出结果为:
There are 100 apples
The area of a circle with radius 2.400000 is 18.095570
字符串类型支持的操作
字符串类型支持使用关系操作符进行比较,支持使用 + 进行拼接。下面的例子展示了字符串类型的判等和拼接:
main() {
let s1 = "abc"
var s2 = "ABC"
let r1 = s1 == s2
println("The result of 'abc' == 'ABC' is: ${r1}")
let r2 = s1 + s2
println("The result of 'abc' + 'ABC' is: ${r2}")
}
编译并执行上述代码,输出结果为:
The result of 'abc' == 'ABC' is: false
The result of 'abc' + 'ABC' is: abcABC
字符串还支持其他常见操作,例如拆分、替换等。
元组类型
元组(Tuple)可以将多个不同的类型组合在一起,成为一个新的类型。元组类型使用 (T1, T2, …, TN) 表示,其中 T1 到 TN 可以是任意类型,不同类型间使用逗号(,)连接。元组至少是二元以上,例如,(Int64, Float64) 表示一个二元组类型,(Int64, Float64, String) 表示一个三元组类型。
元组的长度是固定的,即一旦定义了一个元组类型的实例,它的长度不能再被更改。
元组类型是不可变类型,即一旦定义了一个元组类型的实例,它的内容不能再被更新。例如
var tuple = (true, false)
tuple[0] = false // Error, 'tuple element' can not be assigned
元组类型的字面量
元组类型的字面量使用 (e1, e2, …, eN) 表示,其中 e1 到 eN 是表达式,多个表达式之间使用逗号分隔。下面的例子中,分别定义了一个 (Int64, Float64) 类型的变量 x,以及一个 (Int64, Float64, String) 类型的变量 y,并且使用元组类型的字面量为它们定义了初值:
let x: (Int64, Float64) = (3, 3.141592)
let y: (Int64, Float64, String) = (3, 3.141592, "PI")
元组支持通过 t[index] 的方式访问某个具体位置的元素,其中 t 是一个元组,index 是下标,并且 index 只能是从 0 开始且小于元组元素个数的整数类型字面量,否则,编译报错。下面的例子中,使用 pi[0] 和 pi[1] 可以分别访问二元组 pi 的第一个元素和第二个元素。
main() {
var pi = (3.14, "PI")
println(pi[0])
println(pi[1])
}
编译并执行上述代码,输出结果为:
3.140000
PI
在赋值表达式中,可使用元组字面量对表达式的右值进行解构,这要求赋值表达式等号左边必须是一个元组字面量,这个元组字面量里面的元素必须都是左值(左值即出现在赋值操作符左边的,可保存值的表达式,具体参见各章节对赋值操作的描述)或者一个元组字面量,当元组字面量中出现 _ 时,表示忽略等号右侧 tuple 对应位置处的求值结果(意味着这个位置处的类型检查总是可以通过的),等号右边的表达式也必须是 tuple 类型,右边 tuple 每个元素的类型必须是对应位置左值类型的子类型。注意,复合赋值不支持这种解构方式。求值顺序上先计算等号右边表达式的值,再对左值部分从左往右逐个赋值,例如:
var a: Int64
var b: String
var c: Unit
var f = { => ((1, "abc"), ())}
((a, b), c) = f() // value of a is 1, value of b is "abc", value of c is '()'
((a, b), _) = ((2, "def"), 3.0) // value of a is 2, value of b is "def", 3.0 is ignored
元组类型的类型参数
可以为元组类型标记显式的类型参数名,下面例子中的 name 和 price 就是 类型参数名。
func getFruitPrice (): (name: String, price: Int64) {
return ("banana", 10)
}
对于一个元组类型,只允许统一写类型参数名,或者统一不写类型参数名,不允许交替存在。
let c: (name: String, Int64) = ("banana", 5) // Error
数组类型
Array
我们可以使用 Array 类型来构造单一元素类型,有序序列的数据。
仓颉使用 Array 来表示 Array 类型。T 表示 Array 的元素类型,T 可以是任意类型。
var a: Array<Int64> = ... // Array whose element type is Int64
var b: Array<String> = ... // Array whose element type is String
元素类型不相同的 Array 是不相同的类型,所以它们之间不可以互相赋值。
因此以下例子是不合法的。
b = a // Type mismatch
我们可以轻松使用字面量来初始化一个 Array,只需要使用方括号将逗号分隔的值列表括起来即可。
编译器会根据上下文自动推断 Array 字面量的类型。
let a: Array<String> = [] // Created an empty Array whose element type is String
let b = [1, 2, 3, 3, 2, 1] // Created a Array whose element type is Int64, containing elements 1, 2, 3, 3, 2, 1
也可以使用构造函数的方式构造一个指定元素类型的 Array。
需要注意的是,当通过 item 指定的初始值初始化 Array 时,该构造函数不会拷贝 item,如果 item 是一个引用类型,构造后数组的每一个元素都将指向相同的引用。
let a = Array<Int64>() // Created an empty Array whose element type is Int64
let b = Array<Int64>(a) // Use another Array to initialize b
let c = Array<Int64>(3, item: 0) // Created an Array whose element type is Int64, length is 3 and all elements are initialized as 0
let d = Array<Int64>(3, {i => i + 1}) // Created an Array whose element type is Int64, length is 3 and all elements are initialized by the initialization function
访问 Array 成员
当我们需要对 Array 的所有元素进行访问时,可以使用 for-in 循环遍历 Array 的所有元素。
Array 是按元素插入顺序排列的,因此对 Array 遍历的顺序总是恒定的。
main() {
let arr = [0, 1, 2]
for (i in arr) {
println("The element is ${i}")
}
}
编译并执行上面的代码,会输出:
The element is 0
The element is 1
The element is 2
当我们需要知道某个 Array 包含的元素个数时,可以使用 size 属性获得对应信息。
main() {
let arr = [0, 1, 2]
if (arr.size == 0) {
println("This is an empty array")
} else {
println("The size of array is ${arr.size}")
}
}
编译并执行上面的代码,会输出:
The size of array is 3
当我们想访问单个指定位置的元素时,可以使用下标语法访问(下标的类型必须是 Int64)。非空 Array 的第一个元素总是从位置 0 开始的。我们可以从 0 开始访问 Array 的任意一个元素,直到最后一个位置(Array 的 size - 1)。索引值不能使用负数或者大于等于 size,当编译器能检查出索引值非法时,会在编译时报错,否则会在运行时抛异常。
main() {
let arr = [0, 1, 2]
let a = arr[0] // a == 0
let b = arr[1] // b == 1
let c = arr[-1] // array size is '3', but access index is '-1', which would overflow
}
如果我们想获取某一段 Array 的元素,可以在下标中传入 Range 类型的值,就可以一次性取得 Range 对应范围的一段 Array。
let arr1 = [0, 1, 2, 3, 4, 5, 6]
let arr2 = arr1[0..5] // arr2 contains the elements 0, 1, 2, 3, 4
当 Range 字面量在下标语法中使用时,我们可以省略 start 或 end。
当省略 start 时,Range 会从 0 开始;当省略 end 时,Range 的 end 会延续到最后一位。
let arr1 = [0, 1, 2, 3, 4, 5, 6]
let arr2 = arr1[0..5] // arr2 contains the elements 0, 1, 2, 3, 4
修改 Array
Array 是一种长度不变的 Collection 类型,因此 Array 没有提供添加和删除元素的成员函数。
但是 Array 允许我们对其中的元素进行修改,同样使用下标语法。
main() {
let arr = [0, 1, 2, 3, 4, 5]
arr[0] = 3
println("The first element is ${arr[0]}")
}
编译并执行上面的代码,会输出:
The first element is 3
Array 是引用类型,因此 Array 在作为表达式使用时不会拷贝副本,同一个 Array 实例的所有引用都会共享同样的数据。
因此对 Array 元素的修改会影响到该实例的所有引用。
let arr1 = [0, 1, 2]
let arr2 = arr1
arr2[0] = 3
// arr1 contains elements 3, 1, 2
// arr2 contains elements 3, 1, 2
VArray
仓颉编程语言引入了值类型数组 VArray<T, N > ,其中 T 表示该值类型数组的元素类型, N> ,其中 T 表示该值类型数组的元素类型, N>,其中T表示该值类型数组的元素类型,N 是一个固定的语法,通过 $ 加上一个数值字面量表示这个值类型数组的长度。需要注意的是,VArray<T, $N> 不能省略 <T, $N>,且使用类型别名时,不允许拆分 VArray 关键字与其泛型参数。
type varr1 = VArray<Int64, $3> // ok
type varr2 = VArray // Error
注意
由于运行时后端限制,当前 VArray<T, $N> 的元素类型 T 或 T 的成员不能包含引用类型、枚举类型、Lambda 表达式(CFunc 除外)以及未实例化的泛型类型。
VArray 可以由一个数组的字面量来进行初始化,左值 a 必须标识出 VArray 的实例化类型:
var a: VArray<Int64, $3> = [1, 2, 3]
同时,它拥有两个构造函数:
// VArray<T, $N>(initElement: (Int64) -> T)
let b = VArray<Int64, $5>({ i => i}) // [0, 1, 2, 3, 4]
// VArray<T, $N>(item!: T)
let c = VArray<Int64, $5>(item: 0) // [0, 0, 0, 0, 0]
除此之外,VArray<T, $N> 类型提供了两个成员方法:
-
用于下标访问和修改的 [] 操作符方法:
var a: VArray<Int64, $3> = [1, 2, 3] let i = a[1] // i is 2 a[2] = 4 // a is [1, 2, 4]
-
用于获取 VArray 长度的 size 成员:
var a: VArray<Int64, $3> = [1, 2, 3] let s = a.size // s is 3
区间类型
区间类型用于表示拥有固定步长的序列,区间类型是一个泛型(详见泛型章节),使用 Range 表示。当 T 被实例化不同的类型时(要求此类型必须支持关系操作符,并且可以和 Int64 类型的值做加法),会得到不同的区间类型,如最常用的 Range 用于表示整数区间。
每个区间类型的实例都会包含 start、end 和 step 三个值。其中,start 和 end 分别表示序列的起始值和终止值,step 表示序列中前后两个元素之间的差值(即步长);start 和 end 的类型相同(即 T 被实例化的类型),step 类型是 Int64,并且它的值不能等于 0。
下面的例子给出了区间类型的实例化方式(关于区间类型定义和其中的属性,详见《仓颉编程语言库 API》):
// Range<T>(start: T, end: T, step: Int64, hasStart: Bool, hasEnd: Bool, isClosed: Bool)
let r1 = Range<Int64>(0, 10, 1, true, true, true) // r1 contains 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
let r2 = Range<Int64>(0, 10, 1, true, true, true) // r2 contains 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
let r3 = Range<Int64>(10, 0, -2, true, true, false) // r3 contains 10, 8, 6, 4, 2
区间类型字面量
区间字面量有两种形式:“左闭右开”区间和“左闭右闭”区间。
- “左闭右开”区间的格式是 start…end : step,它表示一个从 start 开始,以 step 为步长,到 end(不包含 end)为止的区间;
- “左闭右闭”区间的格式是 start…=end : step,它表示一个从 start 开始,以 step 为步长,到 end(包含 end)为止的区间。
下面的例子定义了若干区间类型的变量:
let n = 10
let r1 = 0..10 : 1 // r1 contains 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
let r2 = 0..=n : 1 // r2 contains 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
let r3 = n..0 : -2 // r3 contains 10, 8, 6, 4, 2
let r4 = 10..=0 : -2 // r4 contains 10, 8, 6, 4, 2, 0
区间字面量中,可以不写 step,此时 step 默认等于 1,但是注意,step 的值不能等于 0。另外,区间也有可能是空的(即不包含任何元素的空序列),举例如下:
let r5 = 0..10 // the step of r5 is 1, and it contains 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
let r6 = 0..10 : 0 // Error, step cannot be 0
let r7 = 10..0 : 1 // r7 to r10 are empty ranges
let r8 = 0..10 : -1
let r9 = 10..=0 : 1
let r10 = 0..=10 : -1
注意
- 表达式 start…end : step 中,当 step > 0 且 start >= end,或者 step < 0 且 start <= end 时,start…end : step 是一个空区间;
- 表达式 start…=end : step 中,当 step > 0 且 start > end,或者 step < 0 且 start < end 时,start…=end : step 是一个空区间。
Unit类型
对于那些只关心副作用而不关心值的表达式,它们的类型是 Unit。例如,print 函数、赋值表达式、复合赋值表达式、自增和自减表达式、循环表达式,它们的类型都是 Unit。
Unit 类型只有一个值,也是它的字面量:()。除了赋值、判等和判不等外,Unit 类型不支持其他操作。
Noting类型
Nothing 是一种特殊的类型,它不包含任何值,并且 Nothing 类型是所有类型的子类型。
break、continue、return 和 throw 表达式的类型是 Nothing,程序执行到这些表达式时,它们之后的代码将不会被执行。其中 break、continue 只能在循环体中使用,return 只能在函数体中使用。
包围着的循环体“无法穿越”函数边界。在下面的例子中,break 出现在函数 f 中,外层的 while 循环体不被视作包围着它的循环体;continue 出现在 lambda 表达式中,外层的 while 循环体不被视作包围着它的循环体。
while (true) {
func f() {
break // Error, break must be used directly inside a loop
}
let g = { =>
continue // Error, continue must be used directly inside a loop
}
}
由于函数的形参和其默认值不属于该函数的函数体,所以下面例子中的 return 表达式缺少包围它的函数体——它既不属于外层函数 f(因为内层函数定义 g 已经开始),也不在内层函数 g 的函数体中:
func f() {
func g(x!: Int64 = return) { // Error, return must be used inside a function body
0
}
1
}
注意
目前编译器还不允许在使用类型的地方显式地使用 Nothing 类型。