“不可变”优先

不可变(Immutable)指的是在变量赋值或对象创建结束之后,使用者就不能再改变它的值或状态。不可变意味着只读不写,因此不可变对象天然地具备线程安全的特性,即如无其它特殊限制的话可以在任何线程上自由调用。此外,相较于可变对象,不可变对象的访问没有副作用,因此在一些场合下也会让程序更易于了解,而且提供较高的安全性。

不可变通常可以分为两种,一种是不可变变量,不可变变量是指经初始化后其值就不可被修改的变量;另一种是不可变类型,不可变类型是指在构造完成后实际数据对象的内容无法被改变。

在仓颉中,let 定义的变量是不可变变量,而像 String、enum 等类型是不可变类型,这些都是不可变思想在仓颉中的应用。更多地使用不可变特性可以让程序更安全,也更利于理解和维护。

函数参数不可变

在仓颉中,所有函数形参都是不可变的,这意味着我们无法对形参赋值,如果形参是值类型,也无法修改形参的成员。

struct Point {
    var x: Int
    var y: Int
    init(x: Int, y: Int) { ... }
    ...
}

func f(a: Point) {  // a 不可变
    a = Point(0, 0) // error
    a.x = 2 // error
}

模式匹配引入的新变量不可变

在仓颉中,模式匹配支持变量绑定模式,我们可以将目标值解析到新绑定的变量中,但这个变量仍然是不可变的。这意味着我们无法对绑定的变量赋值,如果变量是值类型,也无法修改变量的成员。

func f(a: ?Point) {
    match (a) {
        case Some(b) => //b 不可变
            b = Point(0, 0) // error
            b.x = 2 // error
        case None => ()
    }
}

闭包捕获可变变量不允许逃逸

在仓颉中,闭包指的自包含的函数或 lambda,闭包可以从定义它的静态作用域中捕获变量,即使对闭包调用不在定义的作用域,仍可以访问其捕获的变量。

仓颉中允许闭包捕获可变变量,但不允许该闭包继续逃逸,这避免了对可变变量修改可能导致的意外行为。

func f() {
    let a = 1
    var b = 2
    func g() {
        print(a) // ok
        print(b) // ok
    }
    return g // error, g 捕获了可变变量 b,g 不允许作为表达式使用。
}