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
, andString
types (excluding interpolated strings) Array
literals whose elements are allconst
expressions (the elements cannot be of theArray
type but can be of theVArray
type) andtuple
literals whose elements are allconst
expressionsconst
variables, parameters ofconst
functions, and local variables ofconst
functionsconst
functions, including functions whose names are declared usingconst
,lambda
expressions that meet the requirements ofconst
functions, and function expressions returned by these functions- calls to
const
functions (includingconst
constructors) whose expressions and arguments are allconst
expressions - calls to
enum
constructors whose parameters are allconst
expressions, andenum
constructors without parameters - Arithmetic expressions, relational expressions, and bitwise operation expressions of the numeric,
Bool
,Unit
,Rune
, andString
types, whose operands are allconst
expressions if
,match
,try
,is
, andas
expressions and control transfer expressions (includingreturn
,break
,continue
, andthrow
), whose sub-expressions are allconst
expressions- Member access expressions (excluding those for attribute access) and index access expressions of the
tuple
type inconst
expressions this
andsuper
expressions inconst init
andconst
functions- Function calls of
const
instance member functions ofconst
expressions, where all arguments areconst
expressions
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
const
function must be modified byconst
. - Global
const
andstatic const
functions can only access those external variables that are declared byconst
, including globalconst
variables and staticconst
member variables.const init
functions andconst
instance member functions can access the external variables declared byconst
as well as the instance member variables of the current type. - All expressions in
const
functions exceptconst init
functions must beconst
expressions. - In
const
functions,let
andconst
can be used to declare new local variables. However,var
is not supported. - There are no special requirements on parameter types and return types in
const
functions. If the actual parameter of a function call does not meet the requirements of aconst
expression, the function call cannot be used as aconst
expression, but can still be used as a common expression. const
functions are not necessarily executed during compilation. For example, they can be called in non-const
functions during running.- The overloading rules of
const
functions are the same as those of non-const
functions. - The numeric,
Bool
,Unit
,Rune
,String
, andenum
types can be used to defineconst
instance member functions. - For
struct
andclass
,const
instance member functions can be defined only afterconst init
is defined.const
instance member functions inclass
cannot beopen
.const
instance member functions instruct
cannot bemut
.
In addition, const
functions can be defined in APIs. The rules are as follows:
- The implementation type of a
const
function in an API must also be aconst
function. - For a non-
const
function in an API, the implementation type can be aconst
or non-const
function. - Similar to a
static
function, aconst
function 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
class
types, aconst init
function can only be defined if there are novar
member variables. If the current type has a parent class, anyconst init
functions in it must call aconst init
function of the parent class (explicitly or implicitly if aconst init
function without parameters is available). If the parent class does not have aconst init
function, an error is reported. - If an instance member variable of the current type has an initial value, the initial value must be a
const
expression. Otherwise, aconst init
function cannot be defined. - Only assignment expressions can be used to assign values to instance member variables in a
const init
function.
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).