Namsoo CHO
Intro to Rust
Published in
3 min readApr 23, 2020

--

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에 대하여 살펴볼 것이다.

--

--