Rust 에러 E0435: attempt to use a non-constant value in a constant (상수에 상수가 아닌 값 사용)
E0435는 컴파일하는 동안 알아야 하는 것이, 프로그램이 실행될 때만 존재하는 값을 쓰려고 한다는 뜻이에요. 가장 흔한 곳은 [0.0; days] 같은 배열 길이, 또는 let 변수를 읽는 const예요. 코드만 보면 아무 문제 없어 보이고, 다른 많은 언어에서는 실제로 잘 돌아가요. 하지만 Rust에서 상수와 배열 길이는 프로그램이 시작되기도 전에 컴파일러가 계산해 두는 값이라서, 평범한 변수를 볼 수 없어요. 아주 흔한 에러이고, 대부분 단어 하나만 바꾸면 고쳐져요. 이 페이지는 Rust 컴파일러 에러 해설의 일부예요.
에러 메시지
섹션 제목: “에러 메시지”error[E0435]: attempt to use a non-constant value in a constant --> src/main.rs:3:26 |3 | let rainfall = [0.0; days]; // ❌ 배열 길이는 컴파일할 때 알아야 해요 | ^^^^ non-constant value |help: consider using `const` instead of `let` |2 - let days = 7;2 + const days: /* Type */ = 7; |이 에러를 내는 프로그램은 다음과 같아요.
fn main() { let days = 7; let rainfall = [0.0; days]; // ❌ 배열 길이는 컴파일할 때 알아야 해요 println!("{:?}", rainfall);}무슨 뜻인가요
섹션 제목: “무슨 뜻인가요”^^^^는 3번째 줄, [0.0; days] 안의 days를 가리켜요. 이건 배열의 “반복” 형태로, 0.0을 일곱 개 넣은 배열이에요. ; 뒤에 오는 부분이 배열의 길이이고, 길이는 배열 타입의 일부예요. [f64; 7]과 [f64; 8]은 서로 다른 타입이에요. 그래서 rustc는 컴파일하는 동안 그 숫자를 알아야 해요.
그런데 days는 let 변수예요. rustc에게 let은 프로그램이 실행되는 동안에만 존재하는 값이에요. 바로 옆에 그냥 7이라고 써 놓았더라도요. “non-constant value”(상수가 아닌 값)가 바로 그 뜻이에요.
help:는 해결책을 보여 줘요. 2번째 줄을 const로 바꾸라는 거예요. /* Type */ 자리 표시에 주목하세요. const에는 항상 타입을 직접 써야 하니까, 실제로는 const DAYS: usize = 7;이라고 쓰게 돼요.
const나 static이 let 변수를 읽을 때도 같은 제목 줄이 나와요. 예를 들어 const PRICE_WITH_TAX: f64 = base_price * 1.2; 같은 경우요. 이때 help:는 반대 방향을 제안해요. const 대신 let을 쓰라고요.
신청서를 인쇄하는 것과 비슷해요. 신청서에 칸이 몇 개 있을지는 누가 내용을 채우기 전에, 인쇄소에서 정해져요. 나중에 누군가 신청서에 적을 답에 따라 칸 수를 바꿀 수는 없어요.
Rust가 이렇게 하는 이유
섹션 제목: “Rust가 이렇게 하는 이유”Rust에는 두 시점이 있어요. cargo build가 코드를 프로그램으로 바꾸는 컴파일 타임, 그리고 그 프로그램이 돌아가는 런타임이에요. const, static, 배열 길이는 모두 컴파일 타임에 정해져요. 컴파일러가 그 값을 프로그램 안에 바로 써 넣어요. 그래서 빠르고, 어디서든 쓸 수 있어요.
let 변수는 런타임에 속해요. 그 값은 파일에서 올 수도 있고, 사용자 입력에서 올 수도 있고, 코드가 실행될 때만 하는 계산에서 올 수도 있어요. let days = 7;이 마침 리터럴이라고 해서 Rust가 예외를 두지는 않아요. 그래야 규칙이 단순하게 유지되고, 언젠가 7을 read_days_from_config()로 바꾸는 날에도 코드가 갑자기 깨지지 않아요.
그러니 스스로 물어볼 질문은 하나예요. 코드를 쓰는 지금, 이 숫자를 알고 있나요? 알고 있다면 const로 만드세요. 모른다면 배열 길이가 될 수 없고, Vec이 알맞은 도구예요. 상수와 배열과 벡터를 참고하세요.
고치는 방법
섹션 제목: “고치는 방법”방법 1: 크기를 const로 만들기
섹션 제목: “방법 1: 크기를 const로 만들기”한 주의 7일처럼 숫자가 코드에 고정되어 있을 때 이 방법을 고르세요. help:가 말한 대로 하되, 두 가지만 바꾸세요. 타입을 쓰고(길이라면 usize), 이름을 대문자(UPPER_CASE)로 지으세요. 그게 Rust의 상수 이름 스타일이에요.
const DAYS: usize = 7; // ✅ 컴파일할 때 알 수 있어요
fn main() { let mut rainfall = [0.0; DAYS]; rainfall[2] = 4.5; rainfall[5] = 1.0; println!("{:?}", rainfall); println!("{}일", rainfall.len());}실행 결과:
[0.0, 0.0, 4.5, 0.0, 0.0, 1.0, 0.0]7일const를 main 안, 원래 let이 있던 자리에 둬도 돼요. 파일 맨 위에 두면 모든 함수가 쓸 수 있어요. 소문자 이름 days를 그대로 두어도 컴파일은 되지만, 상수 이름은 대문자여야 한다는 경고가 나와요.
방법 2: 크기를 실행 중에만 알 수 있다면 Vec 쓰기
섹션 제목: “방법 2: 크기를 실행 중에만 알 수 있다면 Vec 쓰기”숫자가 실행 중인 프로그램에서 올 때 이 방법을 고르세요. 리스트의 길이, 파일에서 읽은 값, 사용자 입력, 함수 매개변수 같은 것들이요. 배열은 크기가 고정되어 있고, Vec은 어떤 크기든 될 수 있는, 늘어나는 리스트예요. vec![value; n]도 같은 “반복” 형태인데, 여기서는 n이 어떤 변수든 될 수 있어요.
fn main() { let players = ["Ana", "Ben", "Chen"]; let rounds = players.len() * 2;
let mut scores = vec![0; rounds]; // ✅ Vec의 길이는 변수에서 와도 돼요 scores[0] = 10; scores[3] = 7; println!("{:?}", scores); println!("{}라운드", scores.len());}실행 결과:
[10, 0, 0, 7, 0, 0]6라운드생각보다 훨씬 자주 이게 정답이에요. Vec은 배열이 쓰이는 거의 모든 곳에서 똑같이 쓸 수 있어요. 인덱싱, .len(), 반복문, 슬라이스(&scores[1..3])까지요. 평범한 프로그램에서는 속도 차이도 아주 작아요. 벡터를 참고하세요.
방법 3: const 대신 let 쓰기
섹션 제목: “방법 3: const 대신 let 쓰기”const(또는 static)가 let 변수를 읽을 때 이 방법을 고르세요. 이 경우의 에러는 이렇게 생겼어요.
error[E0435]: attempt to use a non-constant value in a constant --> src/main.rs:3:33 |3 | const PRICE_WITH_TAX: f64 = base_price * 1.2; // ❌ `const`는 `let` 변수를 읽을 수 없어요 | ^^^^^^^^^^ non-constant value |help: consider using `let` instead of `const`문제가 있는 프로그램은 다음과 같아요.
fn main() { let base_price = 20.0; const PRICE_WITH_TAX: f64 = base_price * 1.2; // ❌ `const`는 `let` 변수를 읽을 수 없어요 println!("{:.2}", PRICE_WITH_TAX);}값이 프로그램이 실행되는 동안 계산된다면 그건 평범한 변수니까, let으로 만드세요(이름은 소문자로). 두 값이 정말로 둘 다 고정되어 있다면 반대 방향도 돼요. 첫 번째 값도 const로 만드는 거예요. 아래에 두 가지를 모두 보여 줄게요.
const BASE_PRICE: f64 = 20.0;const PRICE_WITH_TAX: f64 = BASE_PRICE * 1.2; // ✅ const는 다른 const를 쓸 수 있어요
fn main() { let base_price = 35.0; let price_with_tax = base_price * 1.2; // ✅ let은 무엇이든 쓸 수 있어요 println!("{:.2}", price_with_tax); println!("{:.2}", PRICE_WITH_TAX);}실행 결과:
42.0024.00help: 줄은 let PRICE_WITH_TAX를 제안해요. 그대로 해도 컴파일은 되지만, 변수 이름은 snake_case여야 한다는 경고가 나와요. price_with_tax로 이름을 바꾸세요.
방법 4: 정말 배열이 필요하다면 const 제네릭이나 const fn 쓰기
섹션 제목: “방법 4: 정말 배열이 필요하다면 const 제네릭이나 const fn 쓰기”진짜 고정 크기 배열이 필요한데, 그 크기가 함수 바깥에서 와야 하거나 계산되어야 할 때 이 방법을 고르세요. 함수 매개변수도 런타임 값이라서, 이 코드는 E0435를 내요(rustc는 매개변수가 would need to be a const, 즉 const여야 한다고 말해요).
error[E0435]: attempt to use a non-constant value in a constant --> src/main.rs:2:21 |1 | fn empty_row(size: usize) { | ---- this would need to be a `const`2 | let row = [0u8; size]; // ❌ `size`는 함수가 실행될 때만 알 수 있어요 | ^^^^문제가 있는 프로그램은 다음과 같아요.
fn empty_row(size: usize) { let row = [0u8; size]; // ❌ `size`는 함수가 실행될 때만 알 수 있어요 println!("{:?}", row);}
fn main() { empty_row(3);}const 제네릭(const generics)은 rustc가 요구하는 그대로 해 줘요. <const N: usize>는 크기를 컴파일 타임 매개변수로 만들어요. empty_row::<5>()처럼 직접 쓰거나, 타입에서 알아내게 할 수 있어요. const fn 함수는 컴파일러가 컴파일 타임에 실행할 수 있는 함수라서, 상수를 계산하는 데 쓸 수 있어요.
fn empty_row<const N: usize>() -> [u8; N] { [0; N] // ✅ N은 컴파일 타임 매개변수예요}
const fn cells(width: usize, height: usize) -> usize { width * height}
const WIDTH: usize = 4;const HEIGHT: usize = 3;const CELLS: usize = cells(WIDTH, HEIGHT); // ✅ 컴파일러가 계산해요
fn main() { let row: [u8; 3] = empty_row(); let wide = empty_row::<5>(); println!("{:?}", row); println!("{:?}", wide);
let board = [0u8; CELLS]; println!("칸 {}개", board.len());}실행 결과:
[0, 0, 0][0, 0, 0, 0, 0]칸 12개stable Rust에는 한 가지 제한이 있어요. 제네릭 함수 안에서 N을 그대로 길이로 쓰는 건 되지만, [u8; N * 2]처럼 N으로 계산한 값은 안 돼요. 그러면 generic parameters may not be used in const operations(제네릭 매개변수는 const 연산에 쓸 수 없다)라는 에러가 나고, nightly 전용 기능이 필요해요. 런타임 값 size로 empty_row(size)를 부르는 쪽이 나라면, 방법 2로 돌아가세요. 제네릭을 참고하세요.
자주 보게 되는 곳
섹션 제목: “자주 보게 되는 곳”.len()이나 사용자 입력에서 온 크기. let n = names.len(); let done = [false; n];은 E0435를 내요. n은 런타임에만 알 수 있으니까요. 해결: let done = vec![false; n];(방법 2).
let을 읽는 static. static LINE_WIDTH: usize = width;도 같은 제목 줄을 내고, help: consider using `let` instead of `static` 가 붙어요. static도 const처럼 컴파일 타임에 정해져요. 해결: let을 쓰거나, width도 const로 만드세요.
제네릭 함수 안의 const. fn bytes<T>() -> usize { const SIZE: usize = std::mem::size_of::<T>(); SIZE }에서 const는 T를 쓸 수 없어요. 이건 다른 코드인 E0401이고, 해결 아이디어는 같아요. let size = std::mem::size_of::<T>();라고 쓰세요.
관련 에러
섹션 제목: “관련 에러”- E0434: can’t capture dynamic environment in a fn item: 함수 안에 쓴
fn에 적용되는, 같은 “아이템은 지역 변수를 볼 수 없다” 규칙이에요. - E0401: can’t use generic parameters from outer item: 안쪽
fn이나const가 바깥 함수의T를 쓸 때 나요. - E0015:
cannot call non-const function `args` in constants.const가 변수를 읽는 대신std::env::args()같은 평범한 함수를 부를 때 나요. 해결:let이나const fn을 쓰세요. - E0308: mismatched types: 방법 1을 적용한 뒤,
[f64; DAYS]가 와야 할 자리에 길이가 다른 배열을 넘기면 나요. - E0658: use of unstable feature: 이 에러를 피하려고 nightly 전용 const 기능에 손을 뻗을 때 나요.
- 모든 컴파일러 에러 해설
이해도 점검
섹션 제목: “이해도 점검”이해도 점검
1. let n = 3; let a = [0; n];은 n이 분명히 3인데도 왜 E0435를 낼까요?
2. 리스트의 크기가 프로그램이 읽는 파일에서 와요. 올바른 해결책은 무엇일까요?
3. 어느 줄이 컴파일될까요?
점수: 0 / 3
더 알아보기
섹션 제목: “더 알아보기”- E0435 — Rust error codes index (영어):
const로 고치는 방법이 나오는 공식 설명이에요. - 상수 — The Rust Programming Language 한국어판:
const가let과 어떻게 다른지, 왜 상수에는 타입이 필요한지 알려 줘요. - Constant evaluation — The Rust Reference (영어): 컴파일러가 컴파일 타임에 계산할 수 있는 표현식이 정확히 무엇인지, 그리고 어디서 그게 필요한지(배열 길이,
const,static) 설명해요. - 벡터에 여러 값의 목록 저장하기 — The Rust Programming Language 한국어판: 크기를 런타임에만 알 수 있을 때 쓰는, 늘어나는 리스트예요.
- Const generics MVP — Rust Blog (영어):
<const N: usize>가 어떻게 동작하는지 배열 예제와 함께 보여 주고, stable Rust에서 되는 것과 안 되는 것을 설명해요.