const Functions and Constant Evaluation
Constant evaluation allows certain forms of expressions to be evaluated at compile time, reducing the computational load required during program execution. This chapter primarily introduces the usage methods and rules of constant evaluation.
const Context and const Expressions
A const context refers to the initialization expression of a const variable, where these expressions are always evaluated at compile time. Therefore, restrictions must be placed on the expressions allowed in const contexts to avoid side effects such as modifying global state or performing I/O operations, ensuring they can be evaluated at compile time.
A const expression possesses the capability to be evaluated at compile time. Expressions that satisfy the following rules are considered const expressions:
- Literals of numeric types,
Bool,Unit,Rune, andStringtypes (excluding interpolated strings). Arrayliterals (notArraytype, butVArraytype can be used) andtupleliterals where all elements areconstexpressions.constvariables,constfunction parameters, and local variables withinconstfunctions.constfunctions, including functions declared withconst,lambdaexpressions that meetconstfunction requirements, and function expressions returned by these functions.constfunction calls (includingconstconstructors), where the function expression must be aconstexpression and all arguments must beconstexpressions.enumconstructor calls where all arguments areconstexpressions, and parameterlessenumconstructors.- Arithmetic expressions, relational expressions, and bitwise operation expressions of numeric types,
Bool,Unit,Rune, andStringtypes, where all operands must beconstexpressions. if,match,try,throw,return,is, andasexpressions, where all internal expressions must beconstexpressions.- Member access of
constexpressions (excluding property access) and index access oftuple. const initandthisandsuperexpressions withinconstfunctions.constinstance member function calls ofconstexpressions, where all arguments must beconstexpressions.
Note:
The current compiler implementation does not support using
throwas aconstexpression.
const Functions
const functions are a special category of functions that possess the capability to be evaluated at compile time. When these functions are called in a const context, they are executed during compilation. In other non-const contexts, const functions behave like ordinary functions and are executed at runtime.
The following example demonstrates a const function that calculates the distance between two points on a plane. The distance function uses let to define two local variables, dx and dy:
struct Point {
const Point(let x: Float64, let y: Float64) {}
}
const func distance(a: Point, b: Point) {
let dx = a.x - b.x
let dy = a.y - b.y
(dx ** 2 + dy ** 2) ** 0.5
}
main() {
const a = Point(3.0, 0.0)
const b = Point(0.0, 4.0)
const d = distance(a, b)
println(d)
}
Compilation and execution output:
5.000000
Key points to note:
constfunction declarations must be marked with theconstmodifier.- Global
constfunctions andstatic constfunctions can only access externally declaredconstvariables, includingconstglobal variables andconststatic member variables. Other external variables are inaccessible.const initfunctions andconstinstance member functions can access not onlyconst-declared external variables but also instance member variables of the current type. - All expressions within
constfunctions must beconstexpressions, except forconst initfunctions. constfunctions can useletandconstto declare new local variables but do not supportvar.- There are no special restrictions on the parameter types and return types of
constfunctions. If the arguments of a function call do not meet the requirements of aconstexpression, the function call cannot be used as aconstexpression but can still be used as an ordinary expression. constfunctions are not necessarily executed at compile time; for example, they can be called at runtime within non-constfunctions.- The overloading rules for
constfunctions and non-constfunctions are consistent. - Numeric types,
Bool,Unit,Rune,Stringtypes, andenumsupport definingconstinstance member functions. - For
structandclass,constinstance member functions can only be defined ifconst initis defined.constinstance member functions inclasscannot beopen.constinstance member functions instructcannot bemut.
Additionally, interfaces can also define const functions, but they are subject to the following rules:
- For
constfunctions in an interface, the implementing type must also useconstfunctions to satisfy the interface. - For non-
constfunctions in an interface, the implementing type can use eitherconstor non-constfunctions to satisfy the interface. - Similar to
staticfunctions in interfaces,constfunctions in interfaces can only be used by generic parameters or variables constrained by the interface when the interface is used as a generic constraint.
In the following example, two const functions are defined in interface I, class A implements interface I, and the generic function g has a type parameter upper-bounded by I.
interface I {
const func f(): Int64
const static func f2(): Int64
}
class A <: I {
public const func f() { 0 }
public const static func f2() { 1 }
const init() {}
}
const func g<T>(i: T) where T <: I {
return i.f() + T.f2()
}
main() {
println(g(A()))
}
Compiling and executing the above code outputs:
1
const init
If a struct or class defines a const constructor, then instances of that struct/class can be used in const expressions.
-
If the current type is a
class, it cannot have instance member variables declared withvar; otherwise, definingconst initis not allowed. If the current type has a superclass, theconst initmust call the superclass’sconst init(either explicitly or implicitly by calling a parameterlessconst init). If the superclass does not have aconst init, an error is raised.public class Foo { val a: Int64 = 9 // Error, expected declaration, found 'val' let b: String const init(b: String) { this.b = b } }open public class Boo { let boo: String const init(b: String) { this.boo = b } } public class Foo <: Boo { let c: String const init(c: String) { //Error, there is no non-parameter constructor in super class, please invoke super call explicitly this.c = c } } -
If the instance member variables of the current type have initial values, those initial values must be
constexpressions; otherwise, definingconst initis not allowed.var a = "4123" class Foo { let foo: String = a // Error, expected 'const' expression guaranteed to be evaluated at compile time const init() {} } -
Within
const init, assignment expressions (=) can be used to assign values to instance member variables, but no other assignment expressions (such as+=,-=) are allowed.var a = "4123" class Foo { let foo: String let boo: Int64 const init() { foo = "aa" // OK boo += 10 // Error, variable 'boo' is used before initialization } }
The difference between const init and const functions is that const init allows assignment to instance member variables (using assignment expressions).