Rust 에러 E0434: can't capture dynamic environment in a fn item (안쪽 fn의 변수 캡처)
E0434는 다른 함수 안에 쓴 함수가 바깥 함수의 지역 변수를 쓰려고 했다는 뜻이에요. 될 것처럼 보이죠. 변수가 바로 몇 줄 위에 있으니까요. 하지만 Rust에서 중첩된 fn은 독립된 아이템(item)이라서, 주변의 변수를 볼 수 없어요. 컴파일러가 말하는 “dynamic environment”(동적 환경)는 그냥 “바깥 함수가 실행되는 동안 존재하는 지역 변수들”이라는 뜻이에요. 고치기 가장 쉬운 에러 중 하나예요. rustc가 help: 줄에서 답을 알려 주거든요. 이 페이지는 Rust 컴파일러 에러 해설의 일부예요.
에러 메시지
섹션 제목: “에러 메시지”error[E0434]: can't capture dynamic environment in a fn item --> src/main.rs:5:24 |5 | price * (1.0 + tax_rate) // ❌ 안쪽 `fn`은 `tax_rate`를 볼 수 없어요 | ^^^^^^^^ | = help: use the `|| { ... }` closure form insteadhelp:는 “대신 || { ... } 클로저 형태를 쓰라”는 뜻이에요.
이 에러를 내는 프로그램은 다음과 같아요.
fn main() { let tax_rate = 0.2;
fn with_tax(price: f64) -> f64 { price * (1.0 + tax_rate) // ❌ 안쪽 `fn`은 `tax_rate`를 볼 수 없어요 }
println!("{:.2}", with_tax(10.0));}무슨 뜻인가요
섹션 제목: “무슨 뜻인가요”^^^는 5번째 줄의 tax_rate를 가리켜요. 이건 2번째 줄에서 let으로 만든 main의 지역 변수예요. 안쪽 함수 with_tax가 그걸 읽으려고 하는데, 그건 허용되지 않아요.
help: 줄이 해결책을 알려 줘요. 중첩된 fn 대신 클로저(closure) || { ... }를 쓰라는 거예요. 클로저는 주변의 변수를 쓸 수 있어요. fn은 어디에 쓰든 그럴 수 없어요.
unused variable: `tax_rate` (사용하지 않는 변수 tax_rate)라는 경고도 함께 나오는 경우가 많아요. Rust 입장에서는 그 변수를 정당하게 쓸 수 있는 곳이 없으니까요. 이 경고는 에러를 고치면 같이 사라져요.
책상에서 쓴 봉인된 편지와 비슷해요. 편지를 쓰는 동안 책상 위에 메모가 있었다는 건 상관없어요. 편지를 여는 사람은 봉투 안에 넣은 것만 볼 수 있으니까요.
Rust가 이렇게 하는 이유
섹션 제목: “Rust가 이렇게 하는 이유”중첩된 fn은 파일 맨 위에 쓴 함수와 똑같이 컴파일돼요. main 안에 쓰면 나머지 코드에서 그 이름이 안 보이게 될 뿐이에요. main의 변수와 연결되지는 않아요. 여기에는 이유가 있어요. 평범한 함수에는 그 변수들을 담아 둘 곳이 없거든요. with_tax는 main이 닿는 곳이라면 어디서든 호출할 수 있고, 함수 포인터로 저장할 수도 있고, main의 변수가 사라진 뒤에 호출할 수도 있어요. 평범한 fn은 데이터가 붙어 있지 않은, 코드일 뿐이에요.
클로저는 코드 더하기 캡처한 변수예요. Rust는 그 값(또는 그 값의 참조자)을 클로저 안에 저장하고, 대여 검사기(borrow checker)가 그 값이 충분히 오래 살아 있는지 확인해요. 그래서 rustc가 클로저를 권하는 거예요. 주변 스코프의 변수 캡처하기를 참고하세요.
제네릭 매개변수에도 같은 규칙이 적용돼요. 안쪽 fn은 바깥 함수의 T도 쓸 수 없어요. 그건 E0401이에요.
고치는 방법
섹션 제목: “고치는 방법”방법 1: 클로저 쓰기
섹션 제목: “방법 1: 클로저 쓰기”도우미 코드가 작고 한 함수 안에서만 쓰일 때, 대부분은 이 방법을 고르세요. let 이름 = |매개변수| 본문;이라고 쓰면, 클로저는 스코프 안의 어떤 변수든 쓸 수 있어요.
fn main() { let tax_rate = 0.2;
let with_tax = |price: f64| price * (1.0 + tax_rate); // ✅ 클로저는 `tax_rate`를 볼 수 있어요
println!("{:.2}", with_tax(10.0)); println!("{:.2}", with_tax(24.5));
let prices = [3.0, 7.5, 12.0]; let total: f64 = prices.iter().map(|&p| with_tax(p)).sum(); println!("합계: {:.2}", total);}실행 결과:
12.0029.40합계: 27.00변수를 읽기만 하는 클로저는 그 변수를 빌려요. 그래서 그 뒤에도 tax_rate를 계속 쓸 수 있어요. 클로저 안에서 변수를 바꾼다면 클로저에 let mut이 필요해요. 클로저를 참고하세요.
방법 2: 값을 인자로 넘기기
섹션 제목: “방법 2: 값을 인자로 넘기기”도우미가 진짜 함수로 남아야 할 때 이 방법을 고르세요. 코드가 길거나, 테스트하고 싶거나, 여러 함수가 함께 쓰는 경우예요. 매개변수를 하나 더 주고 값을 넘기세요.
fn with_tax(price: f64, tax_rate: f64) -> f64 { price * (1.0 + tax_rate) // ✅ 이제 `tax_rate`는 매개변수예요}
fn receipt(prices: &[f64], tax_rate: f64) -> f64 { prices.iter().map(|&p| with_tax(p, tax_rate)).sum()}
fn main() { let tax_rate = 0.2; println!("{:.2}", with_tax(10.0, tax_rate)); println!("합계: {:.2}", receipt(&[3.0, 7.5, 12.0], tax_rate));
let reduced_rate = 0.05; println!("{:.2}", with_tax(10.0, reduced_rate));}실행 결과:
12.00합계: 27.0010.50여기서는 함수를 맨 바깥으로 옮겼어요. Rust의 도우미 함수는 대부분 그 자리에 있어요. 중첩된 채로 둬도 동작해요. 중요한 건 값이 인자로 들어온다는 점이에요. 덤으로, 이제 한 가지 세율이 아니라 어떤 세율이든 쓸 수 있어요. 매개변수를 참고하세요.
방법 3: const로 만들기
섹션 제목: “방법 3: const로 만들기”세율처럼 코드에 적혀 있어서 프로그램이 실행되기 전에 값이 정해져 있다면 이 방법을 고르세요. let 변수와 달리 const는 아이템이라서, 중첩됐든 아니든 어떤 함수든 쓸 수 있어요.
const TAX_RATE: f64 = 0.2; // ✅ 상수는 모든 함수에서 보여요
fn main() { fn with_tax(price: f64) -> f64 { price * (1.0 + TAX_RATE) }
println!("{:.2}", with_tax(10.0)); println!("{:.2}", with_tax(24.5));}실행 결과:
12.0029.40이 방법은 컴파일할 때 계산할 수 있는 값에만 통해요. 세율이 파일, 사용자 입력, 다른 변수에서 온다면 const로는 안 돼요. const RATE: f64 = tax_rate;는 다른 에러인 E0435(attempt to use a non-constant value in a constant, 상수에 상수가 아닌 값을 쓰려고 했다)를 내요. 그럴 땐 방법 1이나 방법 2를 쓰세요. 상수를 참고하세요.
자주 보게 되는 곳
섹션 제목: “자주 보게 되는 곳”JavaScript나 Python을 쓰다 온 경우. 그 언어들에서는 중첩된 함수가 바깥 변수를 볼 수 있어서, fn 안의 fn이 자연스럽게 느껴져요. Rust에서 그 역할을 하는 건 클로저예요. 해결: fn helper(x) ... 대신 let helper = |x| ...;라고 쓰세요.
메서드 안의 중첩된 fn에서 self를 쓰는 경우. fn report(&self) { fn show() { println!("{}", self.n); } }도 self를 가리키며 같은 E0434를 내요. 해결: show를 메서드로 만들고(impl 안에 fn show(&self)) self.show()로 호출하거나, 클로저를 쓰세요.
let이 아니라 함수 매개변수인 경우. fn total(prices: &[f64], discount: f64) 안에 중첩된 fn apply(price: f64) -> f64 { price - discount }도 E0434를 내요. 매개변수도 지역 변수거든요. 해결: discount를 apply에 넘기거나, let apply = |price: f64| price - discount;라고 쓰세요.
관련 에러
섹션 제목: “관련 에러”- E0401: can’t use generic parameters from outer item: 타입에 대한 같은 규칙이에요. 안쪽
fn은 바깥의T나Self를 쓸 수 없어요. - E0425: cannot find value in this scope: 이름이 아예 없어요. 오타나, 다른 함수의 변수인 경우예요.
- E0373: closure may outlive the current function: 클로저로 바꾼 뒤, 클로저가 변수를 빌리면서 그 변수보다 오래 살면 만날 수 있어요(해결:
move). - E0424:
selfis not available here: 메서드가 아닌 함수에서self를 썼어요. - E0435:
let변수를 쓰려는const예요. 방법 3에서 봤어요. - 전체 컴파일러 에러: 에러 페이지 전체 목록이에요.
이해도 점검
섹션 제목: “이해도 점검”이해도 점검
1. main 안에 let limit = 10;과 중첩된 fn check(n: i32) -> bool { n < limit }이 있어요. 어떻게 될까요?
2. 지역 변수 limit을 쓸 수 있는 건 어느 것일까요?
3. 값이 실행 중에 사용자 입력으로 들어와요. 어떤 해결책이 통하지 않을까요?
점수: 0 / 3
더 알아보기
섹션 제목: “더 알아보기”- E0434 — Rust error codes index (영어): 클로저로 고치는 법과
const/static으로 고치는 법이 있는 공식 설명이에요. - 클로저 — The Rust Programming Language 한국어판: 클로저가 환경을 어떻게 캡처하는지 설명해요. 중첩된
fn은 할 수 없는 일이에요. - Capturing — Rust by Example (영어): 클로저로 빌리기, 가변으로 빌리기, 이동하기를 작은 예제로 보여 줘요.
- Item declarations — The Rust Reference (영어): 함수 안의 아이템은 그 함수의 매개변수나 지역 변수를 쓸 수 없다는 규칙이에요.
- 상수 — The Rust Programming Language 한국어판:
const가let과 어떻게 다른지, 상수 표현식에 무엇이 들어갈 수 있는지 알려 줘요.