NOTE阅读文档版本:
语言规约 Cangjie-0.53.18-Spec
具体开发指南 Cangjie-LTS-1.0.3
在阅读 了解仓颉的语言规约时, 难免会涉及到一些仓颉的示例代码, 但 我们对仓颉并不熟悉, 所以可以用 仓颉在线体验 快速验证
有条件当然可以直接 配置Canjie-SDK
WARNING博主在此之前, 基本只接触过C/C++语言, 对大多现代语言都没有了解, 所以在阅读过程中遇到相似的概念, 难免会与C/C++中的相似概念作类比, 见谅
此样式内容, 表示文档原文内容
常量求值
const变量
const变量是一种特殊的变量, 它可以定义在编译时完成求值, 并且在运行时不可改变的变量
const变量与let/var声明的变量的区别是必须在定义时就初始化, 且必须用const表达式初始化因此
const变量的类型只能是const表达式支持的类型与
let/var一样,const变量也支持省略类型
const表达式见下文定义const a: Int64 = 0 // okconst b: Int64 // error, b 为初始化const c = f() // error, f() 不是 const 表达式const d = 0 // ok, d 的类型是 Int64func f(): Unit {}
const变量定义后可以像let/var声明的变量一样使用与
let/var声明的变量不同,const由于在编译时就可以得到结果, 可以大幅减少程序运行时需要的计算const a = 0main(): Int64 {let b: Int64 = a // okprint(a) // oklet c: VArray<Int64, $0> = [] // okreturn 0}
const变量可以是全局变量, 局部变量, 静态成员变量
const变量不能在扩展中定义const a = 0 // okclass C {const b = 0 // error, const 成员字段必须由 static 修改static const c = 0 // okvar v: Int64init() {const d = 0 // okv = b + c + d}}extend C {const e = 0 // error, const 不能在 extend 中定义}
const变量可以访问对应类型的所有实例成员, 也可以调用对应类型的所有**非mut**实例成员函数struct Foo {let a = 0var b = 0const init() {}func f1() {}const func f2() {}mut func f3() {b = 123}}main(): Int64 {const v = Foo()print(v.a) // okprint(v.b) // okv.f1() // okv.f2() // okv.f3() // error, f3 是 mut 函数return 0}
const变量初始化后该类型实例的所有成员都是const的(深度const, 包含成员的成员), 因此不能被用于左值struct Foo {let a = 0var b = 0const init() {}}func f() {const v = Foo()v.a = 1 // errorv.b = 1 // error}
const修饰变量, 定义常量, 整个程序不允许修改次变量的值
const变量, 必须在定义时进行初始化
const可以修饰成员变量, 但必须是静态成员变量
const修饰一个实例, 此实例可以访问所有成员变量, 但只能访问非mut成员函数
const修饰一个实例, 此实例的所有成员变量默认会被const修饰, 禁止赋值(当左值)
const表达式
某些特定形式的表达式, 被称为
const表达式, 这些表达式具备了可以在编译时求值的能力在
const上下文中, 这些是唯一允许的表达式, 并且始终会在编译时进行求值而在其它非
const上下文,const表达式不保证在编译时求值以下表达式都是
const表达式
数值类型、
Bool、Unit、Rune、String类型的字面量(不包含插值字符串)所有元素都是
const表达式的array字面量(不能是 Array 类型),tuple字面量
const变量,const函数形参,const函数中的局部变量
const函数, 包含使用const声明的函数名、符合const函数要求的lambda、以及这些函数返回的函数表达式
const函数调用(包含const构造函数), 该函数的表达式必须是const表达式, 所有实参必须都是const表达式所有参数都是
const表达式的enum构造器调用, 和无参数的enum构造器数值类型、
Bool、Unit、Rune、String类型的算数表达式、关系表达式、位运算表达式, 所有操作数都必须是const表达式
if、match、try、控制转移表达式(包含return、break、continue、throw)、is、as这些表达式内的表达式必须都是
const表达式
const表达式的成员访问(不包含属性的访问),tuple的索引访问
const init和const函数中的this和super表达式
const表达式的const实例成员函数调用, 且所有实参必须都是const表达式
首先, 各类型的字面量都是const表达式
const修饰的各种函数、参数、变量等都是const表达式
各种元素是const表达式的运算表达式 等
const表达式应该不是很难区分, 除了let之外, 不能被修改的, 不能被当作左值的等
const上下文
const上下文是一类特定上下文, 在这些上下文内的表达式都必须是const表达式, 并且这些表达式始终在编译时求值
const上下文是指const变量初始化表达式
常量上下文, 始终再编译时求值, 其实是特指const变量初始化表达式
const函数
const函数是一类特殊的函数, 这些函数具备了可以在编译时求值的能力在
const上下文中调用这种函数时, 这些函数会在编译时执行计算而在其它非
const上下文,const函数会和普通函数一样在运行时执行
const函数与普通函数的区别是 限制了部分影响编译时求值的功能,const函数中只能出现声明、const表达式、受限的部分赋值表达式
const函数声明必须使用const修饰全局
const函数和static const函数中只能访问const声明的外部变量, 包含const全局变量、const静态成员变量, 其它外部变量都不可访问
const init函数和const实例成员函数, 除了能访问const声明的外部变量, 还可以访问当前类型的实例成员变量
const函数中的表达式都必须是const表达式,const init函数除外
const函数中可以使用let、const声明新的局部变量, 但不支持var
const函数中的参数类型和返回类型没有特殊规定如果该函数调用的实参不符合
const表达式要求, 那这个函数调用不能作为const表达式使用, 但仍然可以作为普通表达式使用
const函数不一定都会在编译时执行, 例如可以在非const函数中运行时调用
const函数与非const函数重载规则一致数值类型、
Bool、Unit、Rune、String类型 和enum支持定义const实例成员函数对于
struct和class, 只有定义了const init才能定义const实例成员函数
class中的const实例成员函数不能是open的
struct中的const实例成员函数不能是mut的const func f(a: Int64): Int64 { // oklet b = 6if (a > 5 && (b + a) < 15 ) {return a * a}return a}class A {var a = 0}const func f1(a: A): Unit { // okreturn}const func f2(): A {return A() // error, A 没有包含 const init}
const函数使用const修饰, 其内的表达式全部为const表达式(const init除外)
const函数在const上下文中调用时, 会在编译时求值
接口中的const函数
接口中也可以定义
const函数, 但会受到以下规则限制
接口中的
const函数, 实现类型必须也用const函数才算实现接口接口中的非
const函数, 实现类型使用const或非const函数都算实现接口接口中的
const函数与接口的static函数一样, 只有在该接口作为泛型约束的时候, 受约束的泛型变元或变量才能使用这些const函数interface I {const func f(): Int64const static func f2(): Int64}const func g<T>(i: T) where T <: I {return i.f() + T.f2()}
接口中的const函数, 只有实际实现const函数才算实现接口函数
接口中的普通函数, 实际实现const或非const函数, 都算实现接口函数
const init
有无
const init决定了哪些自定义struct/class可以用在const表达式上一个类型能否定义
const init取决于以下条件
如果当前类型是
class, 则不能具有var声明的实例成员变量, 否则不允许定义const init如果当前类型具有父类, 当前的
const init必须调用父类的const init(可以显式调用或者隐式调用无参const init), 如果父类没有const init则报错
Object类型的无参init也是const init, 因此Object的子类可以使用该const init当前类型的实例成员变量如果有初始值, 初始值必须要是
const表达式, 否则不允许定义const init
const init内可以使用赋值表达式对实例成员变量赋值, 除此以外不能有其它赋值表达式
const init与const函数的区别是const init内允许对实例成员变量进行赋值(需要使用赋值表达式)struct R1 {var a: Int64let b: Int64const init() { // oka = 0b = 0}}struct R2 {var a = 0let b = 0const init() {} // ok}func zero(): Int64 {return 0}struct R3 {let a = zero()const init() {} // error, a 的初始化不是 const 表达式}class C1 {var a = 0const init() {} // error, a 不能是 var 绑定}struct R4 {var a = C1()const init() {} // error, a 的初始化不是 const 表达式}open class C2 {let a = 0let b = R2()const init() {} // ok}class C3 <: C2 {let c = 0const init() {} // ok}
如果, class想要实现const init, 那么成员变量不允许存在var变量
且struct/class成员如果存在定义直接初始化, 需要是**const表达式**, 否则不能实现const init
const init内出现的赋值表达式, 只能是对成员变量进行赋值的表达式, 不允许出现其他赋值表达式
const init的作用, 感觉更大是优化性能, 因为在编译时会实例化对象
要实例化一个const变量, 那么此类型就必须实现const init