Rust 컴파일러 에러 해설
Rust 컴파일러가 빨간 에러를 띄웠는데 무슨 말인지 모르겠나요? 잘 찾아오셨어요. Rust 개발자라면 누구나 이런 에러를 봐요. 하루에도 몇 번씩요. 다행히 계속 마주치는 에러는 몇 개 되지 않고, 한 번 알아 두면 더는 무섭지 않아요.
아래의 각 페이지는 에러 코드 하나를 골라서, 실제 에러 메시지를 보여 주고, 쉬운 말로 설명하고, 브라우저에서 바로 실행해 볼 수 있는 해결 방법을 알려 줘요.
가장 자주 보는 에러
섹션 제목: “가장 자주 보는 에러”| 에러 | 컴파일러 메시지 | 쉽게 말하면 |
|---|---|---|
| E0382 | borrow of moved value | 값을 넘겨준 다음에 또 쓰려고 했어요 |
| E0502 | cannot borrow as mutable because it is also borrowed as immutable | 누군가 아직 읽고 있는 값을 바꾸려고 했어요 |
| E0499 | cannot borrow as mutable more than once at a time | 두 곳에서 같은 값을 동시에 바꾸려고 해요 |
| E0308 | mismatched types | 다른 타입이 와야 할 자리에 엉뚱한 타입을 넣었어요 |
| E0277 | the trait bound is not satisfied | 그 타입은 요청한 일(출력, 비교, 더하기 등)을 할 수 없어요 |
| E0106 | missing lifetime specifier | 반환한 참조자가 언제까지 유효한지 Rust가 알 수 없어요 |
| E0425 | cannot find value in this scope | 이름에 오타가 있거나, 그 이름이 있는 블록 밖에서 썼어요 |
| E0599 | no method named … found | 그 타입에 그런 메서드가 없거나, 트레이트를 가져오지(import) 않았어요 |
| E0384 | cannot assign twice to immutable variable | mut로 선언하지 않은 변수를 바꿨어요 |
| E0596 | cannot borrow as mutable, as it is not declared as mutable | mut가 아닌 변수나 참조자를 통해 값을 바꾸려고 했어요 |
| E0597 | borrowed value does not live long enough | 가리키던 값이 사라진 뒤에도 참조자를 쓰고 있어요 |
| E0515 | cannot return reference to local variable | 함수가 끝나면 해제되는 값의 참조자를 반환했어요 |
| E0373 | closure may outlive the current function, but it borrows … | 클로저(주로 스레드)가 빌린 지역 변수가 클로저보다 먼저 사라질 수 있어요 |
| E0369 | binary operation == cannot be applied to type … |
내가 만든 타입이 (아직) ==, <, + 같은 연산자를 지원하지 않아요 |
| E0432 | unresolved import | use 줄이 가리키는 크레이트, 모듈, 항목을 찾을 수 없어요 |
| E0433 | cannot find type … in this scope (older: failed to resolve) | HashMap::new() 같은 경로에 가져오지 않았거나 없는 이름이 있어요 |
| E0424 | self value is a keyword only available in methods with a self parameter |
self를 받지 않는 함수 안에서 self를 썼어요 |
| E0061 | this function takes N arguments but M arguments were supplied | 인자를 너무 많이 또는 적게 넘겼어요 (Rust에는 선택적 인자가 없어요) |
| E0594 | cannot assign to p.hp, as p is not declared as mutable |
mut 권한 없이 필드나 & 뒤의 값을 바꿨어요 |
| E0004 | non-exhaustive patterns: X not covered |
match가 가능한 값을 모두 처리하지 않아요 |
| E0507 | cannot move out of x which is behind a shared reference |
빌리기만 한 값의 소유권을 가져가려고 했어요 |
| E0603 | function x is private |
pub이 아닌 함수, 구조체, 모듈을 썼어요 |
| E0716 | temporary value dropped while borrowed | 그 줄이 끝나면 버려지는 값의 참조자를 계속 들고 있어요 |
| E0463 | can’t find crate for x |
크레이트(또는 대상 플랫폼용 표준 라이브러리)가 설치되어 있지 않아요 |
| E0368 | binary assignment operation += cannot be applied to type … |
내가 만든 타입이 +=, -= 같은 연산자를 지원하지 않아요 |
| E0057 | this function takes N arguments but M were supplied (note: closure defined here) | 클로저를 호출할 때 인자를 너무 많이 또는 적게 넘겼어요 |
| E0282 | type annotations needed | Rust가 타입을 스스로 알아낼 수 없어서 직접 적어 줘야 해요 |
| E0609 | no field x on type T |
필드 이름에 오타가 있거나, 값이 (Option 같은 것에) 감싸져 있어요 |
| E0593 | closure is expected to take a single 2-tuple as argument, but it takes 2 distinct arguments | 튜플 하나(또는 다른 개수의 인자)를 받아야 할 자리에서 클로저가 인자를 따로따로 받아요 |
| E0631 | type mismatch in closure arguments | 클로저 매개변수의 타입이 실제로 받는 값과 달라요 (주로 i32와 &i32) |
| E0005 | refutable pattern in local binding | let Some(x) = …는 실패할 수 있어서 그냥 let으로는 쓸 수 없어요 |
| E0615 | attempted to take value of method len on type String |
s.len() 대신 s.len이라고 썼어요 |
| E0610 | {integer} is a primitive type and therefore doesn’t have fields |
숫자, bool, char에 .필드를 썼어요 |
| E0560 | struct User has no field named nmae |
구조체를 만들 때 그 구조체에 없는 필드 이름을 썼어요 |
| E0616 | field balance of struct Account is private |
모듈 밖에서 pub이 아닌 필드를 읽었어요 |
| E0451 | field x of struct Y is private |
비공개 필드가 있는 구조체를 리터럴로 직접 만들었어요 |
| E0624 | method x is private |
pub이 아닌 메서드나 연관 함수를 호출했어요 |
| E0283 | type annotations needed (cannot infer type … multiple impls satisfy this bound) | 여러 타입이 들어맞을 수 있어서(.collect(), .sum() 등) 하나를 골라 줘야 해요 |
| E0284 | type annotations needed: cannot satisfy <_ as FromStr>::Err == _ |
"42".parse()가 어떤 타입으로 바꿔야 할지 몰라요 |
| E0790 | cannot call associated function on trait without specifying the corresponding impl type |
어떤 타입인지 말하지 않고 Default::default()나 Trait::new()를 썼어요 |
| E0559 | variant Shape::Circle has no field named r |
열거형 배리언트를 만들 때 그 배리언트에 없는 필드 이름을 썼어요 |
| E0063 | missing field email in initializer of User |
구조체를 만들면서 필드를 하나 이상 빠뜨렸어요 |
| E0689 | can’t call method pow on ambiguous numeric type {integer} |
2.pow(10)이나 2.0.sqrt()에는 i32, f64 같은 구체적인 숫자 타입이 필요해요 |
| E0034 | multiple applicable items in scope | 두 트레이트가 같은 이름의 메서드를 줘서 Rust가 하나를 고를 수 없어요 |
| E0412 | cannot find type X in this scope (Rust 1.93+ prints E0425) |
타입 이름에 오타가 있거나, 가져오지(import) 않았거나, 선언하지 않았어요 |
| E0428 | the name area is defined multiple times |
두 항목이 같은 이름을 써요. Rust에는 함수 오버로딩이 없어요 |
| E0026 | variant Shape::Circle does not have a field named r |
패턴에 구조체나 배리언트에 없는 필드 이름을 썼어요 |
| E0027 | pattern does not mention fields id, paid |
구조체 패턴에서 필드를 빠뜨렸어요. 필드를 추가하거나 ..로 끝내세요 |
| E0533 | expected unit struct, unit variant or constant, found struct variant Shape::Circle |
이름만 와야 할 자리에 구조체 배리언트(또는 함수)를 썼어요. { .. }를 붙이세요 |
| E0592 | duplicate definitions with name reset |
두 impl 블록이 같은 이름의 메서드를 정의해요 |
| E0255 | the name Error is defined multiple times |
use로 가져온 이름이 내가 만든 타입이나 함수와 같아요 |
| E0252 | the name Result is defined multiple times |
use 줄 두 개가 같은 이름을 가져와요. 하나를 as로 바꾸세요 |
| E0411 | cannot find type Self in this scope |
impl이나 트레이트 밖에서 Self를 썼어요 |
| E0405 | cannot find trait Display in this scope |
트레이트 이름에 오타가 있거나 가져오지 않았어요(예: use std::fmt::Display;) |
| E0532 | expected unit struct, unit variant or constant, found tuple variant Shape::Circle |
패턴에서 튜플 배리언트(또는 구조체)의 ( )를 빠뜨렸거나, 필요 없는 곳에 ( )를 붙였어요 |
| E0164 | expected tuple struct or tuple variant, found struct variant Shape::Circle |
구조체 배리언트나 연관 항목에 패턴으로 ( )를 썼어요. { }나 가드를 쓰세요 |
| E0404 | expected trait, found derive macro Debug |
impl X for나 T: X의 X가 구조체, 타입 별칭, 또는 가져오지 않은 derive 매크로예요 |
| E0401 | can’t use generic parameters from outer item | 안쪽 fn이 바깥 함수의 T나 Self를 써요. 자기만의 제네릭을 주거나 클로저를 쓰세요 |
| E0107 | enum takes 2 generic arguments but 1 generic argument was supplied | Result<u32>, HashMap<String> 같은 경우예요. < > 안의 인자가 너무 적거나 많아요 |
| E0530 | match bindings cannot shadow tuple variants | use Shape::* 뒤에 쓴 Circle =>처럼, 괄호 없는 배리언트 이름은 새 변수가 될 수 없어요 |
| E0434 | can’t capture dynamic environment in a fn item | 안쪽 fn은 바깥 함수의 지역 변수를 볼 수 없어요. 클로저를 쓰거나 값을 인자로 넘기세요 |
| E0601 | main function not found in crate main |
바이너리에 fn main()이 없어요(빠졌거나, 철자가 틀렸거나, mod 안에 있거나, 사실 라이브러리예요) |
| E0658 | use of unstable library feature iter_intersperse |
안정판 Rust에서 nightly 전용 API를 썼어요. 안정판 대안을 쓰거나 업데이트하세요 |
| E0614 | type u32 cannot be dereferenced |
x가 참조자가 아니라 이미 평범한 값인데 *x라고 썼어요 |
| E0435 | attempt to use a non-constant value in a constant | const, 배열 길이, static이 let 변수를 써요. const로 바꾸거나 Vec을 쓰세요 |
| E0554 | #![feature] may not be used on the stable release channel |
nightly 전용 코드를 복사했어요. 속성을 지우고 안정판 API를 쓰거나, 일부러 nightly를 쓰세요 |
Rust 에러 읽는 법
섹션 제목: “Rust 에러 읽는 법”Rust의 에러 메시지는 친절하기로 유명하지만, 담긴 정보가 많아요. 실제 예를 하나 볼게요.
fn main() { let name = String::from("Ferris"); let greeting = name; println!("{name}");}error[E0382]: borrow of moved value: `name` --> src/main.rs:4:16 |2 | let name = String::from("Ferris"); | ---- move occurs because `name` has type `String`, which does not implement the `Copy` trait3 | let greeting = name; | ---- value moved here4 | println!("{name}"); | ^^^^ value borrowed here after move |help: consider cloning the value if the performance cost is acceptable |3 | let greeting = name.clone(); | ++++++++“이동된 값 name을 빌렸다”는 에러예요. 이렇게 읽어 보세요.
- 첫 줄은 요약이에요. 에러 코드(
E0382)와 한 줄 설명이 있어요. - 화살표 줄(
--> src/main.rs:4:16)은 문제가 생긴 파일, 줄, 칸을 알려 줘요. ^^^^표시는 에러가 난 정확한 위치를 가리켜요.----표시는 거기까지 이어진 사연이에요. 여기서는 값이 2번째 줄에서 만들어지고, 3번째 줄에서 이동(move)되고, 4번째 줄에서 사용됐다고 말하고 있어요.help:줄은 컴파일러가 제안하는 해결책이에요. 추가할 코드를 정확히 보여 줄 때가 많아요(++++표시가 새로 넣을 글자예요). 항상 가장 좋은 방법은 아니지만, 출발점으로는 아주 좋아요.
에러가 여러 개라면 첫 번째 에러부터 읽으세요. 실수 하나가 줄줄이 다른 에러를 만들 수 있어서, 첫 번째를 고치면 나머지가 사라지는 경우가 많아요.
도움이 되는 세 가지 습관
섹션 제목: “도움이 되는 세 가지 습관”-
컴파일러에게 더 물어보세요. 모든 에러 코드에는 더 긴 설명이 들어 있어요. 터미널에서 이렇게 실행해 보세요.
Terminal window rustc --explain E0382짧은 설명과 함께, 에러가 나는 예제와 고친 예제를 보여 줘요(설명은 영어예요).
-
자주 검사하세요.
cargo check는 프로그램 전체를 빌드하지 않고 에러만 찾아 주기 때문에cargo build보다 훨씬 빨라요. 몇 줄 쓸 때마다 실행하면 에러를 스무 개씩이 아니라 하나씩 만나게 돼요. Cargo와 크레이트를 참고하세요. -
에디터가 입력하는 동안 에러를 보여 주게 하세요. rust-analyzer 확장을 쓰면 코드 바로 위에 같은 도움말과 함께 에러가 표시돼요. Rust 도구와 개발 환경 설정을 참고하세요.
Rust에 에러가 이렇게 많은 이유
섹션 제목: “Rust에 에러가 이렇게 많은 이유”이 에러들은 대부분, 다른 언어에서는 실행 중에야 (혹은 영영) 발견되는 문제를 Rust가 프로그램을 실행하기 전에 검사하기 때문에 생겨요. 해제된 메모리 사용, 두 스레드가 같은 데이터를 동시에 바꾸기, null 검사 빠뜨리기 같은 것들이요. 에러 하나를 고칠 때마다, 사용자에게 절대 닿지 않을 버그 하나를 없애는 셈이에요.
그래서 많은 사람이 이렇게 말해요. Rust에서는 싸움이 컴파일할 때 일어나고, 그다음엔 프로그램이 그냥 잘 돌아간다고요. 가장 흔한 에러들 뒤에 있는 규칙은 소유권, 대여, 라이프타임 페이지에서 설명해요.
이해도 점검
섹션 제목: “이해도 점검”이해도 점검
1. Rust 에러 메시지에 문제가 여러 개 나왔어요. 어떤 것부터 고쳐야 할까요?
2. rustc --explain E0382는 무엇을 하나요?
3. 에러 메시지에서 ^^^^ 표시는 무엇을 가리키나요?
점수: 0 / 3
더 알아보기
섹션 제목: “더 알아보기”- Rust Compiler Error Index (영어): 모든 에러 코드의 공식 목록이에요. 코드마다 에러가 나는 예제와 고친 예제가 있어요.
- 소유권 이해하기 — The Rust Programming Language 한국어판: 대여와 이동에 관한 흔한 에러들 뒤에 있는 규칙이에요.
- 에러 처리 — The Rust Programming Language 한국어판: 컴파일 에러가 아니라, 내 프로그램 안에서 에러를 다루는 방법(
Result와panic!)을 설명해요. - What is rustc? — The rustc Book (영어): 컴파일러가 어떻게 동작하는지, 플래그와 린트 레벨까지 설명해요.