상수 항목
상수 항목 (Constant Items)
프로그램 안에서 값이 변하지 않는 이름 있는 상수를 만들고 싶을 때가 있어요. 그럴 때는 const를 쓰는데, 이번 장에서는 상수 항목이 일반 변수와 어떻게 다른지, 언제 평가되는지를 살펴볼게요.
출처: Rust Reference
본문
ConstantItem → const ( IDENTIFIER | _ ) : Type ( = Expression )? ;
상수 항목은 프로그램의 특정 메모리 위치와 연결되지 않은, 선택적으로 이름이 붙은 상수 값이에요.
상수는 본질적으로 어디서든 사용될 때 인라인돼요. 즉 사용될 때 관련 컨텍스트에 바로 복사된다는 뜻이에요. 여기에는 외부 크레이트에서 가져온 상수와 Copy가 아닌 타입의 사용도 포함돼요. 같은 상수에 대한 참조가 반드시 같은 메모리 주소를 가리킨다는 보장은 없어요.
상수 선언은 상수가 위치한 모듈 또는 블록의 값 네임스페이스(value namespace) 에 상수 값을 정의해요.
상수는 명시적으로 타입을 지정해야 해요. 그 타입은 'static 라이프타임이어야 해요. 초기화 표현식의 모든 참조는 'static 라이프타임을 가져야 하고, 상수 타입의 참조는 기본적으로 'static 라이프타임으로 설정돼요. (static 라이프타임 생략 참고)
상수 값이 승격(promotion) 대상이면 그 상수에 대한 참조는 'static 라이프타임을 갖고, 그렇지 않으면 임시 값이 만들어져요.
#![allow(unused)]
fn main() {
const BIT1: u32 = 1 << 0;
const BIT2: u32 = 1 << 1;
const BITS: [u32; 2] = [BIT1, BIT2];
const STRING: &'static str = "bitstring";
struct BitsNStrings<'a> {
mybits: [u32; 2],
mystring: &'a str,
}
const BITS_N_STRINGS: BitsNStrings<'static> = BitsNStrings {
mybits: BITS,
mystring: STRING,
};
}
상수 표현식은 trait 정의에서만 생략할 수 있어요.
소멸자를 가진 상수 (Constants with destructors)
상수에 소멸자(destructor)가 들어갈 수 있어요. 소멸자는 값이 범위를 벗어날 때 실행돼요.
#![allow(unused)]
fn main() {
struct TypeWithDestructor(i32);
impl Drop for TypeWithDestructor {
fn drop(&mut self) {
println!("Dropped. Held {}.", self.0);
}
}
const ZERO_WITH_DESTRUCTOR: TypeWithDestructor = TypeWithDestructor(0);
fn create_and_drop_zero_with_destructor() {
let x = ZERO_WITH_DESTRUCTOR;
// x gets dropped at end of function, calling drop.
// prints "Dropped. Held 0.".
}
}
이름 없는 상수 (Unnamed constant)
연관 상수(associated constant)와 달리, 자유(free) 상수는 이름 대신 밑줄(_)을 써서 이름 없이 만들 수 있어요. 예를 들어:
#![allow(unused)]
fn main() {
const _: () = { struct _SameNameTwice; };
// OK although it is the same name as above:
const _: () = { struct _SameNameTwice; };
}
밑줄 import와 마찬가지로, 매크로는 같은 스코프에서 같은 이름 없는 상수를 여러 번 안전하게 내보낼 수 있어요. 예를 들어 다음은 오류를 만들지 않아야 해요:
#![allow(unused)]
fn main() {
macro_rules! m {
($item: item) => { $item $item }
}
m!(const _: () = (););
// This expands to:
// const _: () = ();
// const _: () = ();
}
평가 (Evaluation)
자유 상수는 패닉을 표면화하기 위해 항상 컴파일 타임에 평가돼요. 이것은 사용되지 않는 함수 안에서도 일어나요:
#![allow(unused)]
fn main() {
// Compile-time panic
const PANIC: () = std::unimplemented!();
fn unused_generic_function<T>() {
// A failing compile-time assertion
const _: () = assert!(usize::BITS == 0);
}
}
더 알아보기 (Learn more)
- 컴파일 타임에 불변인 또 다른 방식인
static과의 차이를 아는 게 중요해요. 상수는 사용처에 인라인되고, static은 메모리 위치를 가져요. - 상수의 타입이
'static이어야 한다는 점은 라이프타임과 연결되니, static 라이프타임 생략 규칙도 함께 보면 좋아요.