constant (상수)와 const fn
const keyword, const fn, and miri
개요
러스트엔 const
키워드가 있습니다. 이름 그대로 상수 선언 키워드며, 얼핏 보면 static item
키워드와 비슷해 보입니다.
주제는 const
이기 때문에 static
의 간단한 설명과 차이점만 보고 넘어갑시다:
1
2
static STATIC: &str = "Hello, World!";
const CONSTANT: &str = "Hello, World!";
둘 모두 &'static str
타입을 가지는 전역 범위에서 사용할 수 있는 상수입니다.
static
: 수명이 있으며, 가변(mut
)이 가능한 변수 (이 경우unsafe
코드로 값을 변경할 수 있습니다.)const
: 변경 불가능. (어떤 일이 있어도 변경할 수 없는 값입니다.)
const fn
는 const
상수처럼 constant context
의 일부입니다. (const impl
등도 이에 포함됩니다.)
이들의 특징은 컴파일 타임 상수 평가자(constant evaluation
)가 컴파일 타임에 표현식을 계산합니다.
또한 이들은 for
반복문 등을 허용하지 않습니다. (후술하겠지만, 사실 for
문 그 자체가 문제는 아닙니다.)
그런데 while
이나 loop
반복문은 사용할 수 있습니다. 이는 Iterator
의 next
함수 때문입니다.
for
반복문은 Iterator
의 next
를 호출하여 순회합니다:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
struct Range(/* start */ usize, /* end */ usize);
impl Iterator for Range {
type Item = usize;
fn next(&mut self) -> Option<Self::Item> {
println!("calling next()");
if self.0 < self.1 {
let result = self.0;
self.0 += 1;
Some(result)
} else {
None
}
}
}
fn main() {
for i in Range(0, 10) {
println!("{i}");
}
}
하지만 const fn
내부에선 const fn
이 아닌 함수를 실행할 수 없습니다. (next
는 const fn
이 아닙니다.)
그렇기에 for
반복문을 사용할 수 없는 것이죠. 때문에 아래의 코드는 작동하지 않습니다:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
const fn foo() -> i32 {
let mut x = 0;
loop { // work
if x == 10 {
break;
}
x += 1;
}
x
}
const fn bar() -> i32 {
for x in 0..10 { // <- `for` is not allowed in a `const fn` ...
if x == 10 {
break;
}
}
}
const FOO: i32 = foo();
const BAR: i32 = bar();
fn main() {
println!("{}", FOO);
println!("{}", BAR);
}
miri
추가로, 러스트의 constant context
는 miri
라는 컴파일러 내장되어있는 인터프리터가 평가합니다.
miri
는 Undefined Behavior
(UB
) 가 일어나면 컴파일 에러를 띄워주기도 합니다.
평가가 완료되면 바이러니에 바이트채로 저장되어, static
등에 저장됩니다.
C++를 해보셨다면, const fn
은 C++의 constexpr
과 상당히 흡사하다는 걸 알 수 있습니다.