Const fn에 대하여…
러스트는 컴파일 타임에 함수의 값을 계산하여 상수처럼 사용하게끔 해주는 좋은 기능을 갖고 있다.
const fn
이 그것이다.
이 글이 쓰이는 시점에 이 기능은 아직 stable 상태가 아니다. 따라서 이 기능을 쓰려면 nightly 버젼 컴파일러를 사용해야 한다.
const fn
을 사용한 예를 아래에 나타낸다.
#![feature(const_fn)]
const fn to_celsius(x: i32) -> i32 {
((x as f64 - 32.0) / 1.8) as i32
}
const FAHR_75: i32 = 75;
const CELS_F75: i32 = to_celsius(FAHR_75);
fn main() {
println!("75F equals {}C", CELS_F75);
}
독자가 상상하듯 const fn
은 컴파일 타임에 함수가 실행된다. 따라서 이 기능은 런타임 비용이 전혀 없다. 이런 특징은 C++에서의 constexpr
과 유사하다.
만약 우리가 복잡한 연산을 거쳐서 값이 나오는 상수를 쓸 필요가 있다면, 이 기능을 쓸 좋은 기회이다.
또한 만약 컴파일 타임에 크기를 알 수 있는 데이터, 예를 들어 배열 같은 데이터를 다루고, 배열의 길이와 관련된 값을 계산하려 할 때, 이 기능은 매우 유용하다. 이런 경우에 러스트 컴파일러는 컴파일 타임에 모든 값을 결정할 수 있기 때문이다.
const fn
을 쓰려면 함수는 상수 컨텍스트와 상수 아규먼트를 사용해야 한다. 즉, 식이나 문장의 모든 요소가 컴파일 타임에 결정이 가능해야 한다.
이것은 macros
와 다르다.
macros
는 단순히 문자열을 다른 문자열로 컴파일 타임에 대치한다. 매크로는 식을 연산하지 않는다. 다만 메타-코드로 부터 다른 코드를 생산할 뿐이다. 이런 면에서 실제 연산을 수행하여 값을 도출하는 const fn
과 다르다.
다음의 코드를 보자.
macro_rules! to_celsius (
($value:expr) => (
(($value as f64 - 32.0) / 1.8) as i32
)
);
const FAHR_75: i32 = 75;
const CELS_F75: i32 = to_celsius!(FAHR_75);
fn main() {
println!("75F equals {}C", CELS_F75);
}
두 가지 방식의 코드를 실행한 결과는 같다. 그러나 결과를 도출하는 과정은 다르다. 매크로가 사용된 경우, 러스트 컴파일러는 매크로 확장을 통해서 다음의 코드를 먼저 생성한다.
const FAHR_75: i32 = 75;
const CELS_F75: i32 = ((FAHR_75 as f64 - 32.0) / 1.8) as i32;
fn main() {
println!("75F equals {}C", CELS_F75);
}
그 다음 러스트 컴파일러는 화씨 온도를 계산한다. 위의 경우 실행 비용은 두 가지 방식이 같다. 하지만 코드를 보면 알 수 있듯이 const fn
을 사용한 경우가 훨씬 더 간단하고 가독성이 좋은 코드 임을 보여 준다.
다음은 const generics
에 대하여 살펴볼 것이다.