Rust 에러 E0034: multiple applicable items in scope (같은 이름의 메서드 충돌)
E0034는 내 타입에 이름이 같은 메서드가 두 개 있고(두 트레이트에서 하나씩), 그걸 book.describe()처럼 짧게 호출했다는 뜻이에요. Rust 눈에는 둘 다 보이고, 그중 하나를 대신 골라 주지 않아요. 타입이 잘못된 건 전혀 없어요. 어느 트레이트를 말하는지만 알려 주면 돼요. 보통 한 줄이면 되고, 그 줄도 rustc가 써 줘요. 이 페이지는 Rust 컴파일러 에러 해설의 일부예요.
에러 메시지
섹션 제목: “에러 메시지”error[E0034]: multiple applicable items in scope --> src/main.rs:28:25 |28 | println!("{}", book.describe()); // ❌ Summary의 것일까요, Label의 것일까요? | ^^^^^^^^ multiple `describe` found |note: candidate #1 is defined in an impl of the trait `Label` for the type `Book` --> src/main.rs:21:5 |21 | fn describe(&self) -> String { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^note: candidate #2 is defined in an impl of the trait `Summary` for the type `Book` --> src/main.rs:15:5 |15 | fn describe(&self) -> String { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^help: disambiguate the method for candidate #1 |28 - println!("{}", book.describe()); // ❌ Summary의 것일까요, Label의 것일까요?28 + println!("{}", Label::describe(&book)); // ❌ Summary의 것일까요, Label의 것일까요? |help: disambiguate the method for candidate #2 |28 - println!("{}", book.describe()); // ❌ Summary의 것일까요, Label의 것일까요?28 + println!("{}", Summary::describe(&book)); // ❌ Summary의 것일까요, Label의 것일까요? |“multiple applicable items in scope”는 “스코프 안에 적용할 수 있는 항목이 여러 개 있다”는 뜻이에요. 아래에서 한 줄씩 풀어 볼게요.
이 에러를 내는 프로그램은 다음과 같아요.
trait Summary { fn describe(&self) -> String;}
trait Label { fn describe(&self) -> String;}
struct Book { title: String, pages: u32,}
impl Summary for Book { fn describe(&self) -> String { format!("{} ({}쪽)", self.title, self.pages) }}
impl Label for Book { fn describe(&self) -> String { format!("BOOK: {}", self.title.to_uppercase()) }}
fn main() { let book = Book { title: String::from("Rust for Crabs"), pages: 320 }; println!("{}", book.describe()); // ❌ Summary의 것일까요, Label의 것일까요?}무슨 뜻인가요
섹션 제목: “무슨 뜻인가요”메시지를 위에서 아래로 읽어 보세요.
describe아래의^^^^^^^^: “multipledescribefound”(describe가 여러 개 있다). Rust가Book에서describe라는 메서드를 찾았더니 하나가 넘었어요.- 두
note:줄은 후보(candidate)를 나열해요. 하나는impl Label for Book(21번째 줄)에서, 다른 하나는impl Summary for Book(15번째 줄)에서 와요. 둘 다 진짜이고 둘 다 동작하지만, 돌려주는 글은 달라요. - 두
help:줄은 “후보 #1(또는 #2)로 메서드를 명확히 하라”며 각각 고친 코드를 보여 줘요.Summary::describe(&book)은 “Summary에서 온describe를,&book을self로 해서 호출해”라는 뜻이에요.
한 사무실에 “민수”라는 동료가 두 명 있다고 생각해 보세요. “민수 씨!“라고 부르면 곤란하죠. “회계팀 민수 씨”라고 불러야 해요. Summary::describe가 바로 “회계팀 민수 씨”예요.
Rust가 이렇게 하는 이유
섹션 제목: “Rust가 이렇게 하는 이유”book.describe() 같은 메서드 호출은 지름길이에요. Rust는 book의 타입을 보고, 그 타입 자신의 메서드와, 스코프 안에 있으면서 Book에 구현된 모든 트레이트의 메서드를 찾아봐요. 보통은 딱 하나가 나와요. 여기서는 두 개가 나왔고, 하는 일도 달라요. 하나는 요약을, 다른 하나는 서가 라벨을 출력해요.
Rust가 “먼저 정의된 트레이트”처럼 숨은 규칙으로 하나를 고를 수도 있었을 거예요. 하지만 그러면 트레이트를 하나 추가하거나 use 두 줄의 순서만 바꿔도 프로그램이 출력하는 내용이 아무 말 없이 바뀌어 버려요. Rust는 추측하지 않고, 코드를 읽는 모두가 볼 수 있도록 코드 안에 한 번 적어 달라고 해요.
예외가 하나 있어요. 타입 자신의 impl Book 블록에 있는 메서드는 같은 이름의 트레이트 메서드보다 항상 우선해요. 그래서 그 경우에는 E0034가 나지 않아요. 트레이트와 고급 트레이트에서 더 읽어 볼 수 있어요.
고치는 방법
섹션 제목: “고치는 방법”방법 1: 트레이트를 통해 호출하기: Trait::method(&x)
섹션 제목: “방법 1: 트레이트를 통해 호출하기: Trait::method(&x)”두 메서드가 다 필요하고, 매번 어느 쪽인지 말하고 싶을 때 이 방법을 고르세요. rustc가 제안하는 것과 똑같아요. 값은 첫 번째 인자로 넣고, 메서드의 &self가 기대하는 대로 &를 붙여요.
trait Summary { fn describe(&self) -> String;}
trait Label { fn describe(&self) -> String;}
struct Book { title: String, pages: u32,}
impl Summary for Book { fn describe(&self) -> String { format!("{} ({}쪽)", self.title, self.pages) }}
impl Label for Book { fn describe(&self) -> String { format!("BOOK: {}", self.title.to_uppercase()) }}
fn main() { let book = Book { title: String::from("Rust for Crabs"), pages: 320 }; println!("{}", Summary::describe(&book)); println!("{}", Label::describe(&book));}실행 결과:
Rust for Crabs (320쪽)BOOK: RUST FOR CRABS메서드가 &mut self를 받는다면 &mut book을 넘기세요. self를 받는다면 book을 넘기세요(점으로 호출할 때처럼 값이 이동해요).
방법 2: 완전 정규화 문법: <Type as Trait>::method()
섹션 제목: “방법 2: 완전 정규화 문법: <Type as Trait>::method()”충돌하는 함수에 self가 없을 때 이 방법을 고르세요. 생성자나 fn category() 같은 함수요. 그러면 Rust가 살펴볼 값이 없으니까 Trait::category()만으로는 부족하고, 타입도 말해 줘야 해요. <Book as Summary>::category()는 “Book이 Summary를 구현해서 갖게 된 category“라고 읽어요. 메서드에도 쓸 수 있어요.
trait Summary { fn category() -> String; fn describe(&self) -> String;}
trait Label { fn category() -> String;}
struct Book { title: String,}
impl Summary for Book { fn category() -> String { String::from("논픽션") } fn describe(&self) -> String { format!("제목이 {}인 책", self.title) }}
impl Label for Book { fn category() -> String { String::from("B 서가") }}
fn main() { let book = Book { title: String::from("Rust for Crabs") }; println!("{}", <Book as Summary>::category()); println!("{}", <Book as Label>::category()); println!("{}", <Book as Summary>::describe(&book));}실행 결과:
논픽션B 서가제목이 Rust for Crabs인 책여기서 Book::category()라고 쓰면 역시 E0034가 나고, use fully-qualified syntax to disambiguate(완전 정규화 문법으로 명확히 하라)라는 help가 붙어요. Summary::category()만 쓰면 다른 에러인 E0790이 나요. 트레이트는 여전히 어느 타입을 말하는지 모르니까요.
방법 3: 메서드 하나의 이름 바꾸기
섹션 제목: “방법 3: 메서드 하나의 이름 바꾸기”두 트레이트가 모두 내 것이고 이름이 겹친 게 우연일 때 이 방법을 고르세요. 하는 일이 다른 두 메서드라면 이름도 달라야 마땅하고, 그러면 짧은 book.describe()가 다시 동작해요. 가장 깔끔한 해결책일 때가 많아요.
trait Summary { fn describe(&self) -> String;}
trait Label { fn label_text(&self) -> String;}
struct Book { title: String, pages: u32,}
impl Summary for Book { fn describe(&self) -> String { format!("{} ({}쪽)", self.title, self.pages) }}
impl Label for Book { fn label_text(&self) -> String { format!("BOOK: {}", self.title.to_uppercase()) }}
fn main() { let book = Book { title: String::from("Rust for Crabs"), pages: 320 }; println!("{}", book.describe()); println!("{}", book.label_text());}실행 결과:
Rust for Crabs (320쪽)BOOK: RUST FOR CRABS물론 다른 크레이트에 있는 트레이트의 메서드 이름은 바꿀 수 없어요. 그럴 때는 방법 1이나 방법 4를 쓰세요.
방법 4: 필요한 트레이트만 가져오기
섹션 제목: “방법 4: 필요한 트레이트만 가져오기”트레이트들이 서로 다른 모듈이나 크레이트에 있을 때 이 방법을 고르세요. 트레이트의 메서드는 그 트레이트가 스코프 안에 있을 때만 점 문법으로 쓸 수 있어요. 그러니 코드의 어떤 부분에서 하나만 필요하다면, 거기서는 그것만 가져오세요. use를 함수 안에 넣어서 그 함수에서만 적용되게 할 수도 있어요.
mod report { pub trait Summary { fn describe(&self) -> String; }}
mod shop { pub trait Label { fn describe(&self) -> String; }}
struct Book { title: String,}
impl report::Summary for Book { fn describe(&self) -> String { format!("제목이 {}인 책", self.title) }}
impl shop::Label for Book { fn describe(&self) -> String { format!("BOOK: {}", self.title.to_uppercase()) }}
fn print_label(book: &Book) { use shop::Label; // 여기서는 Label만 스코프 안에 있어요 println!("{}", book.describe());}
fn main() { use report::Summary; // 여기서는 Summary만 스코프 안에 있어요 let book = Book { title: String::from("Rust for Crabs") }; println!("{}", book.describe()); print_label(&book);}실행 결과:
제목이 Rust for Crabs인 책BOOK: RUST FOR CRABSimpl 블록에서는 report::Summary와 shop::Label을 전체 경로로 썼다는 점을 눈여겨보세요. 트레이트를 구현할 때는 use가 필요 없고, 점으로 호출할 때만 필요해요. use에 대해서는 모듈을 참고하세요.
자주 보게 되는 곳
섹션 제목: “자주 보게 되는 곳”직접 만든 to_string(). Display를 구현한 타입은 모두 표준 라이브러리의 ToString 트레이트에서 to_string()을 이미 받아요. 여기에 to_string 메서드를 가진 내 트레이트를 추가하면 point.to_string()이 E0034를 내고, candidate #2 is defined in an impl of the trait `ToString` for the type `T` (후보 #2는 T 타입에 대한 ToString 트레이트 구현에 정의돼 있다)가 나와요. 해결: 내 메서드의 이름을 바꾸거나(pretty(), to_label()), Pretty::to_string(&point)로 호출하세요.
prelude::*를 두 개 가져올 때. 라이브러리는 트레이트들을 가져오도록 use some_crate::prelude::*;를 제공하는 경우가 많아요. 같은 타입에 draw()나 render() 메서드를 추가하는 prelude 두 개를 가져오면 점 호출이 모호해져요. 해결: 글롭(*) 하나를 실제로 쓰는 항목만으로 바꾸거나, Render::draw(&data)로 호출하세요.
바운드가 두 개인 제네릭 함수. fn show<T: Summary + Label>(item: &T)에서 item.describe()는 E0034를 내요. 여기서는 rustc의 제안을 조심하세요. Label::describe(&item)을 제안하지만, item은 이미 참조자라서 그러면 E0277이 나요(the trait bound `&T: Label` is not satisfied, &T가 Label 트레이트 바운드를 만족하지 않는다). 남는 & 없이 Label::describe(item)이라고 쓰세요. 제네릭을 참고하세요.
관련 에러
섹션 제목: “관련 에러”- E0790:
Default::default()나Summary::category()처럼self가 없는 트레이트 함수를, 어느 타입인지 말하지 않고 호출했어요. - E0599: 반대 문제예요. 그 이름의 메서드를 하나도 찾지 못했어요. 트레이트를 가져오지 않은 경우가 많아요.
- E0277: 트레이트 바운드를 만족하지 않아요. 예를 들어
Trait::method(&x)에&를 하나 더 붙였을 때요. - E0428: 이름이 여러 번 정의됐어요. 트레이트 메서드 두 개가 아니라, 한 모듈 안에 같은 이름의 항목이 두 개 있는 경우예요.
- 전체 컴파일러 에러: 에러 페이지 전체 목록이에요.
이해도 점검
섹션 제목: “이해도 점검”이해도 점검
1. Book이 Summary와 Label을 구현하고, 둘 다 fn describe(&self)가 있어요. Label의 것을 호출하는 줄은 어느 것일까요?
2. 두 트레이트 모두 self가 없는 fn category() -> String이 있어요. 어느 호출이 동작할까요?
3. Book에는 자신의 impl Book { fn describe(&self) ... }가 있고, describe가 있는 Summary도 구현해요. book.describe()는 어떻게 될까요?
점수: 0 / 3
더 알아보기
섹션 제목: “더 알아보기”- E0034 — Rust error codes index (영어): 두 가지 완전 정규화 형태가 모두 나오는 공식 설명이에요.
- 고급 트레이트 — The Rust Programming Language 한국어판:
Pilot,Wizard,Human예제로 완전 정규화 문법을 설명하는 절이에요. - Disambiguating overlapping traits — Rust by Example (영어): 같은 이름의 메서드를 가진 두 트레이트를 다루는, 짧고 실행 가능한 예제예요.
- 트레이트로 공통된 동작을 정의하기 — The Rust Programming Language 한국어판: 트레이트를 정의하고 구현하는 법, 그리고 메서드를 쓰려면 트레이트가 스코프 안에 있어야 하는 이유를 설명해요.
- Method call expressions — The Rust Reference (영어):
x.method()뒤의 메서드를 찾을 때 Rust가 따르는 정확한 규칙이에요.