const Functions and Constant Evaluation
Constant evaluation allows some specific forms of expressions to be evaluated during compilation, reducing the calculations required during program execution. This section describes the usage and rules of constant evaluation.
const Variables
A const variable is a special variable that is introduced with the const keyword, which defines a variable that is evaluated during compilation and cannot be changed during execution. In the following example, the gravitational constant G is defined:
const G = 6.674e-11
The type annotation can be omitted for const variables, but the initialization expression cannot be omitted. A const variable can be a global variable, local variable, or static member variable. However, it cannot be defined in an extension. It can access all instance members and call all non-mut instance member functions of the corresponding type.
In the following example, a struct is defined to record the mass and radius of a planet, and a const member function gravity calculates the gravitational force of the planet on an object with a mass of m at a distance of r:
struct Planet {
const Planet(let mass: Float64, let radius: Float64) {}
const func gravity(m: Float64, r: Float64) {
G * mass * m / r**2
}
}
main() {
const myMass = 71.0
const earth = Planet(5.972e24, 6.378e6)
println(earth.gravity(myMass, earth.radius))
}
The gravitational force of the Earth on an adult with a mass of 71 kg on the ground is calculated during compilation.
695.657257
After a const variable is initialized, all of its instance members are considered const (transitively including all instance members reachable through it), and therefore cannot be assigned to.
main() {
const myMass = 71.0
myMass = 70.0 // Error, cannot assign to immutable value
}
const Context and const Expressions
The const context refers to initialization expressions of const variables, which are always evaluated during compilation. Only a restricted set of expressions are allowed in the const context to avoid side effects such as global state and I/O and to ensure that they can be evaluated during compilation.
const expressions are those that can be evaluated during compilation and therefore are allowed to appear in a const context. The following are const expressions:
- Literals of the numeric,
Bool,Unit,Rune, andStringtypes (excluding interpolated strings) Arrayliterals whose elements are allconstexpressions (the elements cannot be of theArraytype but can be of theVArraytype) andtupleliterals whose elements are allconstexpressionsconstvariables, parameters ofconstfunctions, and local variables ofconstfunctionsconstfunctions, including functions whose names are declared usingconst,lambdaexpressions that meet the requirements ofconstfunctions, and function expressions returned by these functions- calls to
constfunctions (includingconstconstructors) whose expressions and arguments are allconstexpressions - calls to
enumconstructors whose parameters are allconstexpressions, andenumconstructors without parameters - Arithmetic expressions, relational expressions, and bitwise operation expressions of the numeric,
Bool,Unit,Rune, andStringtypes, whose operands are allconstexpressions if,match,try,is, andasexpressions and control transfer expressions (includingreturn,break,continue, andthrow), whose sub-expressions are allconstexpressions- Member access expressions (excluding those for attribute access) and index access expressions of the
tupletype inconstexpressions thisandsuperexpressions inconst initandconstfunctions- Function calls of
constinstance member functions ofconstexpressions, where all arguments areconstexpressions
const Functions
A const function is a special function that can be evaluated during compilation. When called in a const context, it is evaluated during compilation. In non-const contexts, it is executed during program execution time the same as a normal function.
In the following example, the const function distance calculates the distance between two points on a plane, using 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)
}
The result is as follows:
5.000000
Note:
- The declaration of a
constfunction must be modified byconst. - Global
constandstatic constfunctions can only access those external variables that are declared byconst, including globalconstvariables and staticconstmember variables.const initfunctions andconstinstance member functions can access the external variables declared byconstas well as the instance member variables of the current type. - All expressions in
constfunctions exceptconst initfunctions must beconstexpressions. - In
constfunctions,letandconstcan be used to declare new local variables. However,varis not supported. - There are no special requirements on parameter types and return types in
constfunctions. If the actual parameter of a function call does not meet the requirements of aconstexpression, the function call cannot be used as aconstexpression, but can still be used as a common expression. constfunctions are not necessarily executed during compilation. For example, they can be called in non-constfunctions during running.- The overloading rules of
constfunctions are the same as those of non-constfunctions. - The numeric,
Bool,Unit,Rune,String, andenumtypes can be used to defineconstinstance member functions. - For
structandclass,constinstance member functions can be defined only afterconst initis defined.constinstance member functions inclasscannot beopen.constinstance member functions instructcannot bemut.
In addition, const functions can be defined in APIs. The rules are as follows:
- The implementation type of a
constfunction in an API must also be aconstfunction. - For a non-
constfunction in an API, the implementation type can be aconstor non-constfunction. - Similar to a
staticfunction, aconstfunction in an API can be used by constrained generic variables only when the API is used as a generic constraint.
In the following example, two const functions are defined in the I API implemented by the A class, and the upper bound of the parameter type of the generic function g is 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()))
}
The result is as follows:
1
const init Functions
If a struct or class defines a const constructor, the struct or class instance can be used in a const expression.
- For
classtypes, aconst initfunction can only be defined if there are novarmember variables. If the current type has a parent class, anyconst initfunctions in it must call aconst initfunction of the parent class (explicitly or implicitly if aconst initfunction without parameters is available). If the parent class does not have aconst initfunction, an error is reported. - If an instance member variable of the current type has an initial value, the initial value must be a
constexpression. Otherwise, aconst initfunction cannot be defined. - Only assignment expressions can be used to assign values to instance member variables in a
const initfunction.
The difference between const init functions and const functions is that values can be assigned to instance member variables in const init functions (assignment expressions are required).