콘텐츠로 이동

Rust 에러 E0412: cannot find type in this scope (타입을 찾을 수 없음)

E0412는 Rust가 그 위치에서 볼 수 없는 타입 이름을 썼다는 뜻이에요. 예를 들어 fn print_invoice(inv: &Invoce) 같은 함수 시그니처에서요. 아주 흔하고 쉬운 에러예요. 오타이거나, use 줄이 빠졌거나, 제네릭 <T>를 선언하지 않은 경우예요. 처음에 헷갈릴 수 있는 점이 하나 있어요. Rust 1.93부터 컴파일러는 이 에러를 E0425로 출력해요. 메시지는 똑같이 cannot find type이에요. 이 페이지는 두 코드를 모두 다루고, Rust 컴파일러 에러 해설의 일부예요.

Rust 1.93 이후 버전은 이렇게 출력해요(지금 컴파일러는 1.97이에요).

error[E0425]: cannot find type `Invoce` in this scope
--> src/main.rs:2:24
|
1 | struct Invoice { total: u32 }
| -------------- similarly named struct `Invoice` defined here
2 | fn print_invoice(inv: &Invoce) { // ❌ 오타: Invoce
| ^^^^^^
|
help: a struct with a similar name exists
|
2 | fn print_invoice(inv: &Invoice) { // ❌ 오타: Invoce
| +

“cannot find type Invoce in this scope”는 “이 스코프에서 Invoce라는 타입을 찾을 수 없다”는 뜻이에요. 라벨은 “비슷한 이름의 구조체 Invoice가 여기 정의돼 있다”, help는 “비슷한 이름의 구조체가 있다”는 뜻이에요.

그리고 같은 프로그램을 Rust 1.92 이전 버전에서 컴파일하면 이렇게 나와요. 블로그 글과 Stack Overflow 답변 대부분이 이 버전 시절에 쓰였어요.

error[E0412]: cannot find type `Invoce` in this scope
--> src/main.rs:2:24
|
1 | struct Invoice { total: u32 }
| -------------- similarly named struct `Invoice` defined here
2 | fn print_invoice(inv: &Invoce) {
| ^^^^^^ help: a struct with a similar name exists: `Invoice`

두 에러를 내는 프로그램은 다음과 같아요.

struct Invoice { total: u32 }
fn print_invoice(inv: &Invoce) { // ❌ 오타: Invoce
println!("{}", inv.total);
}
fn main() {
print_invoice(&Invoice { total: 120 });
}

공식 E0412 페이지(영어)는 이제 “this error code is no longer emitted by the compiler”(이 에러 코드는 더 이상 컴파일러가 내지 않는다)로 시작해요. 문제가 사라진 게 아니라, 없는 변수와 함수도 다루는 E0425로 옮겨 간 거예요. 아래 내용은 두 코드 모두에 똑같이 적용돼요.

^^^^^^는 시그니처 안의 Invoce를 가리켜요. 이 함수를 검사하려면 Rust는 Invoce가 무엇인지, 즉 크기가 얼마인지, 어떤 필드가 있는지 알아야 해요. 그래서 2번째 줄에서 보이는 모든 타입(내가 만든 구조체와 열거형, use 줄, 프렐류드)을 살펴봤는데, 그런 이름이 없었던 거예요.

help: 줄을 보면 어떤 상황인지 알 수 있어요.

  • “a struct with a similar name exists”(비슷한 이름의 구조체가 있다): 오타예요. +가 빠진 글자를 보여 줘요.
  • “consider importing this struct”(이 구조체를 가져와 보라): 타입이 다른 곳에 있어서 use 줄이 필요해요.
  • “you might be missing a type parameter”(타입 매개변수가 빠진 것 같다): <T>를 선언하지 않고 제네릭 T를 썼어요.
  • help가 전혀 없음: 그 타입이 아직 없어요. 아직 쓰지 않았거나, Rust가 모르는 파일에 있어요.

“Invoce 200g을 넣으세요”라고 적힌 요리법과 비슷해요. 요리사는 밀가루와 설탕은 알지만 “Invoce”는 몰라서, 시작하기도 전에 멈춰 버려요.

Rust는 프로그램을 실행하기 전에 모든 함수의 시그니처를 검사해요. 매개변수와 반환 타입은 모두 실제로 알려진 타입이어야 해요. Rust는 그 타입으로 값에 필요한 메모리 크기와, 쓸 수 있는 메서드와 필드를 정하거든요. 찾을 수 없는 이름을 추측으로 메우지는 않아요.

use 없이 어디서나 보이는 타입 이름은 몇 개 안 돼요. String, Vec, Option, Result, Box 등이 들어 있는 프렐류드(prelude)예요. 나머지는 모두 직접 정의하거나, use 줄을 쓰거나, std::num::ParseIntError 같은 전체 경로를 써야 해요. 경로와 use가 어떻게 동작하는지는 모듈을 참고하세요.

코드는 왜 바뀌었을까요? Rust 1.93부터 rustc는 없는 타입을 E0425로 알려 줘요. 없는 변수나 함수에 이미 쓰던 코드예요. 메시지에는 여전히 type이라고 나오니까, 어떤 종류의 이름이 없는지는 언제나 알 수 있어요.

방법 1: 철자 고치기, 또는 타입 정의하기

섹션 제목: “방법 1: 철자 고치기, 또는 타입 정의하기”

rustc가 비슷한 이름이 있다고 하거나, help를 전혀 주지 않을 때 이 방법을 고르세요. 타입 이름은 대소문자를 구별해요. invoice, Invoce, Invoice는 서로 다른 세 이름이에요. 비슷한 이름이 없다면 먼저 struct나 enum을 쓰세요.

struct Invoice {
total: u32,
}
fn print_invoice(inv: &Invoice) {
println!("청구서 합계: {} EUR", inv.total);
}
fn main() {
print_invoice(&Invoice { total: 120 });
}

실행 결과:

청구서 합계: 120 EUR
▶ 직접 실행해 보기

방법 2: rustc가 제안하는 use 줄 추가하기

섹션 제목: “방법 2: rustc가 제안하는 use 줄 추가하기”

help가 consider importing this struct(또는 enum, type alias)라고 할 때 이 방법을 고르세요. HashMap, ParseIntError, Ordering처럼 프렐류드 밖에 있는 표준 라이브러리 타입에는 use 줄이 필요해요. 아래 함수는 파싱 에러를 반환해요.

error[E0425]: cannot find type `ParseIntError` in this scope
--> src/main.rs:1:40
|
1 | fn parse_age(text: &str) -> Result<u8, ParseIntError> {
| ^^^^^^^^^^^^^ not found in this scope
|
help: consider importing this struct
|
1 + use std::num::ParseIntError;
|

라벨 not found in this scope는 “이 스코프에서 찾을 수 없다”는 뜻이고, 1 +는 추가할 줄을 보여 줘요.

제안된 줄을 파일 맨 위에 복사하세요.

use std::num::ParseIntError;
fn parse_age(text: &str) -> Result<u8, ParseIntError> {
text.trim().parse()
}
fn main() {
println!("{:?}", parse_age(" 42 "));
println!("{:?}", parse_age("마흔").is_err());
}

실행 결과:

Ok(42)
true
▶ 직접 실행해 보기

시그니처의 타입이 아니라 HashMap::new()(::가 있는 경로)라고 썼다면 대신 E0433이 나고, 같은 use 줄로 고쳐져요. rustc가 가져올 것을 여러 개 제안한다면 고르기 전에 읽어 보세요. 이름만 같은 서로 다른 타입이에요.

방법 3: 제네릭 타입 매개변수 선언하기

섹션 제목: “방법 3: 제네릭 타입 매개변수 선언하기”

help가 you might be missing a type parameter라고 할 때 이 방법을 고르세요. Rust에서 T는 특별한 단어가 아니에요. 내가 지어내는 이름이라서, 함수 이름 뒤의 꺾쇠괄호 안에서 알려 줘야 해요. <T>가 없으면 Rust는 T라는 진짜 타입을 찾아요.

error[E0425]: cannot find type `T` in this scope
--> src/main.rs:1:21
|
1 | fn largest(items: &[T]) -> &T {
| ^ not found in this scope
|
help: you might be missing a type parameter
|
1 | fn largest<T>(items: &[T]) -> &T {
| +++

+++는 largest 뒤에 <T>를 추가하라는 뜻이에요.

T가 나오는 곳마다 같은 에러가 한 번씩 나요. <T>를 추가하고, 함수에 필요한 트레이트도 함께 쓰세요. 항목을 >로 비교하려면 T가 PartialOrd를 구현해야 해요.

fn largest<T: PartialOrd>(items: &[T]) -> &T {
let mut best = &items[0];
for item in items {
if item > best {
best = item;
}
}
best
}
fn main() {
println!("{}", largest(&[34, 50, 25, 100, 65]));
println!("{}", largest(&['y', 'm', 'a', 'q']));
}

실행 결과:

100
y
▶ 직접 실행해 보기

구조체도 마찬가지예요. struct Wrapper { items: Vec<Item> }은 struct Wrapper<Item>이라고 쓰거나 진짜 Item 타입을 정의하지 않으면 실패해요. : PartialOrd를 빼면 대신 >에서 E0369가 나요. 제네릭을 참고하세요.

방법 4: 다른 모듈의 타입 가져오기

섹션 제목: “방법 4: 다른 모듈의 타입 가져오기”

타입은 내가 만든 것인데 다른 모듈에 있을 때 이 방법을 고르세요. 흔히 놀라는 부분이 있어요. 파일 안에 있는 모듈도 파일 맨 위의 타입을 볼 수 없어요. 모듈마다 자기만의 스코프가 있거든요.

error[E0425]: cannot find type `Order` in this scope
--> src/main.rs:7:26
|
7 | pub fn print(order: &Order) -> String {
| ^^^^^ not found in this scope
|
help: consider importing this struct
|
7 + use crate::Order;
|

7 +는 “모듈 안의 7번째 줄에 이 줄을 추가하라”는 뜻이에요. crate::Order는 크레이트 맨 위에서부터의 경로예요. super::Order(“부모 모듈에서”)도 돼요.

struct Order {
id: u32,
total: u32,
}
mod receipts {
use crate::Order;
pub fn print(order: &Order) -> String {
format!("주문 #{}: {} EUR", order.id, order.total)
}
}
fn main() {
let order = Order { id: 7, total: 42 };
println!("{}", receipts::print(&order));
}

실행 결과:

주문 #7: 42 EUR
▶ 직접 실행해 보기

반대 방향도 똑같아요. mod shop 안에 정의한 타입을 main에서 쓰려면 pub struct Product와 use crate::shop::Product;(또는 전체 경로 shop::Product)가 필요해요. 이 상황은 mod tests에서 가장 자주 만나요. 맨 위의 use super::*;가 부모 모듈의 모든 것을 가져오는 이유예요.

선언하지 않은 파일의 타입. src/models.rs 파일은 있는데 mod models; 줄이 없는 상태에서 fn save(user: models::User)라고 쓰면, E0412나 E0425가 아니라 E0433이 나요. cannot find module or crate `models` in this scope(이 스코프에서 models라는 모듈이나 크레이트를 찾을 수 없다)예요. 해결: main.rs 맨 위에 mod models;를 추가하고, 구조체를 pub으로 만드세요.

impl 밖의 Self. 최상위에서 fn new_counter() -> Self라고 쓰면 별도의 코드 E0411이 나요. cannot find type `Self` in this scope(이 스코프에서 Self 타입을 찾을 수 없다)와 함께 `Self` is only available in impls, traits, and type definitions(Self는 impl, 트레이트, 타입 정의 안에서만 쓸 수 있다)라는 라벨이 붙어요. 해결: 함수를 impl Counter { ... } 안으로 옮기거나, Self 대신 Counter라고 쓰세요.

타입 대신 트레이트. Display를 가져오지 않고 impl Display for Point라고 쓰면 E0405, cannot find trait `Display` in this scope(이 스코프에서 Display 트레이트를 찾을 수 없다)가 나고, help: consider importing this trait와 함께 use std::fmt::Display;가 제안돼요. 생각도 해결도 같아요. use 줄을 추가하세요. 트레이트를 참고하세요.

  • E0425: 값이나 함수를 찾을 수 없고, Rust 1.93부터는 타입도 여기에 들어가요. 오늘날 실제로 보게 되는 코드예요.
  • E0433: HashMap::new()나 models::User 같은 경로의 첫 부분을 찾을 수 없어요.
  • E0432: 가져오기를 해결할 수 없어요. use 줄 자체가 아무것도 가리키지 않는 경우예요.
  • E0603: 타입은 찾았지만 pub이 아니에요.
  • E0405(트레이트를 찾을 수 없음)와 E0411(impl 밖의 Self): 위에서 본 가까운 친척이에요.
  • 전체 컴파일러 에러: 에러 페이지 전체 목록이에요.

이해도 점검

1. 따라 하던 튜토리얼에는 error[E0412]: cannot find type이라고 나오는데, 내 컴파일러는 error[E0425]: cannot find type이라고 출력해요. 왜일까요?

2. fn first(items: &[T]) -> &T에서 cannot find type T가 나요. 어떻게 고칠까요?

3. mod billing 안의 함수가 main.rs 맨 위에 정의된 구조체 Order를 써요. 모듈 안에 어떤 줄을 넣으면 고쳐질까요?

점수: 0 / 3