콘텐츠로 이동

Rust 에러 E0405: cannot find trait in this scope (트레이트를 찾을 수 없음)

E0405는 이름을 쓴 트레이트가 그 자리에서 Rust에게 보이지 않는다는 뜻이에요. 대표적인 경우는 이래요. 구조체를 출력할 수 있게 하려고 impl Display for Point라고 썼는데, Display를 가져온(import) 적이 없는 거예요. 트레이트 이름의 오타나, 다른 모듈에 있는 트레이트도 같은 에러를 내요. rustc가 보통 필요한 use 줄을 그대로 알려 주기 때문에 가장 고치기 쉬운 에러 중 하나예요. Rust 1.93에서 E0425로 바뀐 E0412(“cannot find type”)와 달리 이 코드는 그대로예요. rustc 1.97도 트레이트에 대해서는 여전히 E0405를 출력해요. 이 페이지는 Rust 컴파일러 에러 해설의 일부예요.

error[E0405]: cannot find trait `Display` in this scope
--> src/main.rs:6:6
|
6 | impl Display for Point { // ❌ `Display`를 가져온 적이 없어요
| ^^^^^^^ not found in this scope
|
help: consider importing this trait
|
1 + use std::fmt::Display;
|

“cannot find trait Display in this scope”는 “이 스코프에서 Display라는 트레이트를 찾을 수 없다”는 뜻이에요.

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

struct Point {
x: i32,
y: i32,
}
impl Display for Point { // ❌ `Display`를 가져온 적이 없어요
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "({}, {})", self.x, self.y)
}
}
fn main() {
let p = Point { x: 3, y: 4 };
println!("{}", p);
}

^^^^^^^는 6번째 줄의 Display를 가리키고, 라벨은 “not found in this scope”(이 스코프에서 찾을 수 없다)예요. Rust는 그 자리에서 보이는 트레이트를 모두 살펴봤어요. 이 모듈에서 정의한 것, use 줄로 가져온 것, 그리고 작은 묶음인 프렐루드(prelude)에 있는 것이요. 그중 어디에도 Display라는 이름은 없었어요.

help: 줄은 어디에 있는지 알려 줘요. “consider importing this trait”(이 트레이트를 가져오는 걸 고려해 보라), 즉 use std::fmt::Display;예요. 1 +는 “새 1번째 줄로 추가하라”, 즉 파일 맨 위에 넣으라는 뜻이에요.

println!이 늘 Display를 쓰는데 왜 없다고 할까요? 모든 Rust 파일이 공짜로 받는 이름 목록인 프렐루드에는 Clone, Iterator, From, Default 같은 일상적인 트레이트가 들어 있지만, Display, Error, Hash, Write는 들어 있지 않아요. 이것들은 각자의 모듈(std::fmt, std::error, std::hash, std::io)에 있어서, 구현할 때 가져와야 해요.

“Display 팬을 쓰세요”라고 적힌 요리법과 비슷해요. 요리사는 가스레인지 위의 팬은 알지만, 이 팬은 찬장 안에 있고 요리법에는 어느 찬장인지 적혀 있지 않아요.

Rust는 이름을 찾으려고 표준 라이브러리 전체나 프로젝트 전체를 뒤지지 않아요. 스코프 안에 있는 것만 봐요. 내가 만든 항목, use 줄, 그리고 프렐루드요. 그래서 이름이 예측 가능해요. std 안에만 해도 Write와 Error라는 트레이트가 여러 개 있고, 나중에 추가하는 크레이트가 더 가져올 수도 있어요. Rust가 알아서 하나를 고른다면, 의존성 하나를 추가했을 뿐인데 코드의 뜻이 조용히 바뀔 수 있어요.

그래서 어느 트레이트를 뜻하는지 파일 맨 위에서 한 번 말해 줘야 해요. 찾는 일은 help 줄이 대신해 주고, 찾은 트레이트를 제안해 줘요. use가 어떻게 동작하는지는 모듈을, Display는 자주 쓰는 트레이트를 참고하세요.

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

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

help가 consider importing this trait라고 할 때 이 방법을 고르세요. 대부분 이 경우예요. 그 줄을 파일 맨 위에 복사해 넣으세요.

use std::fmt::Display;
struct Point {
x: i32,
y: i32,
}
impl Display for Point {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "({}, {})", self.x, self.y)
}
}
fn main() {
let p = Point { x: 3, y: 4 };
println!("{}", p);
println!("Ferris는 {}에 있어요", Point { x: -1, y: 7 });
}

실행 결과:

(3, 4)
Ferris는 (-1, 7)에 있어요
▶ 직접 실행해 보기

rustc가 가져올 것을 여러 개 제안한다면, 고르기 전에 읽어 보세요. std::fmt::Write와 std::io::Write처럼 이름만 같은 서로 다른 트레이트예요.

방법 2: 모듈을 가져와서 fmt::Display로 쓰기

섹션 제목: “방법 2: 모듈을 가져와서 fmt::Display로 쓰기”

Display를 구현할 때 이 방법을 고르세요. 어차피 같은 모듈의 Formatter와 Result도 필요하거든요. std::fmt를 한 번 가져오고, 셋 모두 앞에 fmt::를 쓰세요. 실제 Rust 코드에서 가장 많이 보게 될 스타일이에요.

use std::fmt;
struct Point {
x: i32,
y: i32,
}
impl fmt::Display for Point {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "({}, {})", self.x, self.y)
}
}
fn main() {
let p = Point { x: 3, y: 4 };
let label = p.to_string();
println!("{}의 길이는 {}글자예요", label, label.len());
}

실행 결과:

(3, 4)의 길이는 6글자예요
▶ 직접 실행해 보기

Display를 구현하면 .to_string()도 공짜로 따라와요. use를 아예 빼고 impl std::fmt::Display for Point라고 써도 돼요. 한 번만 쓸 때는 그것도 괜찮아요.

방법 3: 트레이트 이름의 철자 고치기

섹션 제목: “방법 3: 트레이트 이름의 철자 고치기”

rustc가 a trait with a similar name exists(비슷한 이름의 트레이트가 있다)라고 할 때 이 방법을 고르세요. 트레이트 이름은 대소문자를 구분해서 shape, Shap, Shape는 세 개의 다른 이름이에요.

error[E0405]: cannot find trait `Shap` in this scope
--> src/main.rs:9:6
|
1 | trait Shape {
| ----------- similarly named trait `Shape` defined here
...
9 | impl Shap for Square { // ❌ 오타: Shap
| ^^^^
|
help: a trait with a similar name exists
|
9 | impl Shape for Square { // ❌ 오타: Shap
| +

--- 라벨은 “비슷한 이름의 트레이트 Shape가 여기 정의돼 있다”는 뜻이에요. 문제가 되는 프로그램은 이래요.

trait Shape {
fn area(&self) -> f64;
}
struct Square {
side: f64,
}
impl Shap for Square { // ❌ 오타: Shap
fn area(&self) -> f64 {
self.side * self.side
}
}
fn main() {
let s = Square { side: 3.0 };
println!("{}", s.area());
}

9번째 줄 아래의 +가 빠진 글자를 보여 줘요. 그 글자를 넣으세요.

trait Shape {
fn area(&self) -> f64;
}
struct Square {
side: f64,
}
impl Shape for Square {
fn area(&self) -> f64 {
self.side * self.side
}
}
fn main() {
let s = Square { side: 3.0 };
println!("{}", s.area());
}

실행 결과:

9
▶ 직접 실행해 보기

비슷한 이름도, 가져오라는 제안도 없다면 그 트레이트는 아직 존재하지 않는 거예요. 먼저 trait Shape { ... }로 정의하세요. 트레이트를 참고하세요.

방법 4: 내가 만든 트레이트를 그 모듈에서 가져오기

섹션 제목: “방법 4: 내가 만든 트레이트를 그 모듈에서 가져오기”

트레이트는 내가 만들었지만 다른 모듈에 있을 때 이 방법을 고르세요. 모듈마다 자기만의 스코프가 있어서, mod shapes 안의 트레이트는 가져오기 전까지 main.rs 최상위에서 보이지 않아요.

error[E0405]: cannot find trait `Shape` in this scope
--> src/main.rs:11:6
|
11 | impl Shape for Square { // ❌ `Shape`는 `mod shapes` 안에 있어요
| ^^^^^ not found in this scope
|
help: consider importing this trait
|
1 + use crate::shapes::Shape;
|

문제가 되는 프로그램은 이래요.

mod shapes {
pub trait Shape {
fn area(&self) -> f64;
}
}
struct Square {
side: f64,
}
impl Shape for Square { // ❌ `Shape`는 `mod shapes` 안에 있어요
fn area(&self) -> f64 {
self.side * self.side
}
}
fn main() {
println!("{}", Square { side: 2.0 }.area());
}

use 줄을 추가하고, 트레이트가 pub인지 확인하세요. pub이 없으면 가져오는 줄 자체가 E0603, “trait is private”(트레이트가 비공개다)로 실패해요.

mod shapes {
pub trait Shape {
fn area(&self) -> f64;
}
}
use crate::shapes::Shape;
struct Square {
side: f64,
}
impl Shape for Square {
fn area(&self) -> f64 {
self.side * self.side
}
}
fn main() {
println!("{}", Square { side: 2.0 }.area());
}

실행 결과:

4
▶ 직접 실행해 보기

크레이트의 트레이트도 마찬가지예요. impl Serialize for User를 쓰려면 먼저 use serde::Serialize;가 필요해요. 모듈을 참고하세요.

main이나 도우미 함수의 Box<dyn Error>. 가져오지 않고 fn run() -> Result<(), Box<dyn Error>>라고 쓰면 cannot find trait `Error` in this scope가 나고, help: consider importing this trait와 함께 use std::error::Error;를 제안해요. 해결: 그 줄을 추가하거나 Box<dyn std::error::Error>라고 쓰세요. 사용자 정의 에러를 참고하세요.

제네릭의 트레이트 바운드. fn print_all<T: Display>(items: &[T])도 Display에 대해 같은 E0405를 내고, 바운드를 가리켜요. 해결: use std::fmt::Display;. 제네릭을 참고하세요.

Debug는 다른 코드가 나요. 가져오지 않고 impl Debug for Point나 T: Debug를 쓰면 E0405가 아니라 E0404가 나요. expected trait, found derive macro `Debug` (트레이트를 기대했는데 derive 매크로 Debug를 찾았다)예요. 프렐루드에는 #[derive(Debug)] 매크로는 있지만 Debug 트레이트는 없거든요. 해결: use std::fmt::Debug;(rustc가 제안해요)를 쓰거나, 따로 형식을 만들 필요가 없다면 그냥 #[derive(Debug)]를 쓰세요.

  • E0425: 이 스코프에서 값, 함수, 또는 (Rust 1.93부터) 타입을 찾을 수 없어요.
  • E0412: “cannot find type”의 예전 코드예요. 지금은 E0425로 나와요.
  • E0433: use std::fmt; 없이 쓴 fmt::Display처럼, 경로의 첫 부분을 찾을 수 없어요.
  • E0599: 메서드를 찾을 수 없어요. 그 메서드를 주는 트레이트를 가져오지 않은 경우가 많아요.
  • E0277: 트레이트는 찾았지만, 내 타입이 그 트레이트를 구현하지 않았어요.
  • E0404: 존재하는 이름이지만 트레이트가 아니에요(Debug 매크로처럼). 위에서 봤어요.
  • 전체 컴파일러 에러: 에러 페이지 전체 목록이에요.

이해도 점검

1. impl Display for Invoice라고 썼더니 E0405가 나요. 무엇이 빠졌을까요?

2. 다음 중 use 줄 없이 쓸 수 있는 트레이트는 무엇일까요?

3. 내 트레이트 Shape는 mod shapes에 정의돼 있고, impl Shape for Square는 main.rs에 있어요. 무엇이 필요할까요?

점수: 0 / 3