Rust 에러 E0411: cannot find type Self in this scope (impl 밖의 Self)
E0411은 Rust가 무엇을 가리키는지 알 수 없는 곳에서 Self를 썼다는 뜻이에요. Self는 “이 impl 블록(또는 트레이트)이 다루는 타입”을 줄여 쓴 말이라서, impl, 트레이트, 타입 정의 안에서만 통해요. 흔한 원인은 파일 최상위에 쓴 fn new() -> Self 같은 생성자이거나, impl 블록 밖으로 복사해 낸 메서드예요. 자주 보지만 금방 고칠 수 있어요. Rust 1.93에서 E0425로 바뀐 E0412와 달리 이 코드는 그대로예요. rustc 1.97도 여전히 E0411을 출력해요. 이 페이지는 Rust 컴파일러 에러 해설의 일부예요.
에러 메시지
섹션 제목: “에러 메시지”error[E0411]: cannot find type `Self` in this scope --> src/main.rs:5:21 |5 | fn new_counter() -> Self { // ❌ `impl` 밖에서 쓴 `Self` | ----------- ^^^^ `Self` is only available in impls, traits, and type definitions | | | `Self` not allowed in a function“cannot find type Self in this scope”는 “이 스코프에서 Self라는 타입을 찾을 수 없다”는 뜻이에요.
이 에러를 내는 프로그램은 다음과 같아요.
struct Counter { count: u32,}
fn new_counter() -> Self { // ❌ `impl` 밖에서 쓴 `Self` Counter { count: 0 }}
fn main() { let c = new_counter(); println!("{}", c.count);}무슨 뜻인가요
섹션 제목: “무슨 뜻인가요”rustc는 5번째 줄에서 두 곳을 가리켜요.
Self아래의^^^^: “Selfis only available in impls, traits, and type definitions”(Self는 impl, 트레이트, 타입 정의 안에서만 쓸 수 있다). 규칙을 한 줄로 말해 줘요.new_counter아래의-----: “Selfnot allowed in a function”(함수에서는Self를 쓸 수 없다). 여기서의 이유예요.new_counter는 어느impl에도 속하지 않은 평범한 함수예요.
방금 Counter { count: 0 }이라고 썼으니 new_counter가 Counter를 돌려준다는 건 여러분은 알아요. 하지만 Rust는 Self의 뜻을 짐작하려고 함수 본문을 읽지 않아요. Self는 바깥을 감싸는 impl Counter { ... }나 trait ... { ... }에서만 뜻을 얻는데, 여기에는 그런 게 없어요.
포스트잇에 적힌 “여기”라는 말과 같아요. 지도 위의 “현재 위치: 여기”는 말이 되지만, 식탁 위에 놓인 쪽지의 “여기”가 어디인지는 아무도 몰라요.
Rust가 이렇게 하는 이유
섹션 제목: “Rust가 이렇게 하는 이유”Self는 그 자체로 하나의 타입이 아니에요. 각 impl이나 트레이트가 채워 넣는 자리 표시자예요. impl Counter 안에서 Self는 Counter이고, impl Timer 안에서는 Timer이고, 트레이트 안에서는 “이 트레이트를 구현하는 타입”이에요. 그래서 쓸모가 있어요. 구조체의 이름을 바꾸거나 메서드를 다른 impl로 복사해도 Self가 알아서 따라가요.
자유 함수는 어떤 타입에도 속하지 않아서 채워 넣을 게 없어요. Rust가 본문을 보고 짐작할 수도 있겠지만, 그러면 함수 시그니처가 그 안의 코드에 따라 달라지게 돼요. Rust는 일부러 시그니처를 따로 검사해요. fn new() -> Self가 보통 어디에 있는지는 구조체에서 볼 수 있어요.
고치는 방법
섹션 제목: “고치는 방법”방법 1: 함수를 impl 블록 안으로 옮기기
섹션 제목: “방법 1: 함수를 impl 블록 안으로 옮기기”대부분은 이 방법을 고르세요. 한 타입을 만들거나 다루는 함수는 그 타입의 impl 블록에 두는 게 자연스럽고, 거기서는 Self가 기대한 대로 동작해요. 호출은 Counter::new()로 해요.
struct Counter { count: u32,}
impl Counter { fn new() -> Self { Self { count: 0 } }
fn increment(&mut self) { self.count += 1; }}
fn main() { let mut c = Counter::new(); c.increment(); c.increment(); println!("{}", c.count);}실행 결과:
2impl 안에서는 Self를 타입으로도(-> Self), 값을 만들 때도(Self { count: 0 }) 쓸 수 있어요. 구조체를 참고하세요.
방법 2: Self 대신 타입 이름 쓰기
섹션 제목: “방법 2: Self 대신 타입 이름 쓰기”함수를 자유 함수로 남겨 둬야 할 때 이 방법을 고르세요. 예를 들어 여러 타입을 만드는 도우미 함수이거나, 다른 모듈에 있는 함수라면요. Self라고 쓴 자리에 진짜 타입인 Counter를 쓰면 돼요.
struct Counter { count: u32,}
fn new_counter(start: u32) -> Counter { Counter { count: start }}
fn reset(c: &mut Counter) { c.count = 0;}
fn main() { let mut c = new_counter(10); println!("{}", c.count); reset(&mut c); println!("{}", c.count);}실행 결과:
100impl 밖으로 복사해 낸 fn reset(c: &mut Self)도 이렇게 고쳐요. 시그니처와 본문의 Self를 모두 Counter로 바꾸세요.
방법 3: “아무 타입”을 뜻했다면 제네릭 쓰기
섹션 제목: “방법 3: “아무 타입”을 뜻했다면 제네릭 쓰기”“호출하는 쪽이 원하는 어떤 타입이든”이라는 뜻으로 Self를 썼다면 이 방법을 고르세요. 그건 Self가 아니라 제네릭 T가 할 일이에요. 함수에 필요한 트레이트와 함께 <T>를 선언하세요. 여기서는 T: Default 덕분에 어떤 타입이든 시작값을 만들 수 있어요.
fn fresh_pair<T: Default>() -> (T, T) { (T::default(), T::default())}
fn main() { let numbers: (u32, u32) = fresh_pair(); let words: (String, String) = fresh_pair(); println!("{:?}", numbers); println!("{:?}", words);}실행 결과:
(0, 0)("", "")타입마다 만드는 방법을 스스로 정하게 하고 싶다면 함수를 트레이트에 넣으세요. trait Reset { fn fresh() -> Self; }처럼요. 트레이트 안에서 Self는 “이 트레이트를 구현하는 타입”을 뜻해요. 제네릭과 트레이트를 참고하세요.
자주 보게 되는 곳
섹션 제목: “자주 보게 되는 곳”main이나 자유 함수 안의 Self { ... }. let c = Self { count: 0 };도 E0411을 내는데, 첫 줄이 조금 달라요. cannot find struct, variant or union type `Self` in this scope(이 스코프에서 Self라는 구조체, 배리언트, 공용체 타입을 찾을 수 없다)예요. 해결: Counter { count: 0 }이라고 쓰거나 Counter::new()를 호출하세요.
메서드 안에 중첩된 도우미 함수. impl Counter 안의 메서드가 자기 안에 fn helper() -> Self를 품고 있으면 다른 코드인 E0401이 나요. can't use `Self` from outer item(바깥 항목의 Self를 쓸 수 없다)예요. 함수 안의 함수는 바깥 impl을 물려받지 않아요. 해결: 안쪽 함수에서는 Counter라고 쓰거나, 클로저를 쓰거나, helper를 impl 안의 별도 메서드로 올리세요.
Self와 self 헷갈리기. 대문자 Self는 타입이고, 소문자 self는 메서드를 호출한 값이에요. 메서드 밖에서 self를 쓰면 다른 에러인 E0424가 나요. 둘 다 해결은 같아요. 코드가 알맞은 impl 블록 안에 있는지 확인하세요.
관련 에러
섹션 제목: “관련 에러”- E0424: 메서드 밖에서
self(소문자, 값)를 썼어요. - E0425: 보통 이름의 값, 함수, 또는 (Rust 1.93부터) 타입을 찾을 수 없어요.
- E0412: “cannot find type”의 예전 코드예요. 지금은 E0425로 나와요.
- E0405: 트레이트를 찾을 수 없어요. 보통
use줄이 빠졌어요. - E0401: 바깥
impl의Self를 중첩된 함수 안에서 썼어요. 위에서 봤어요. - 전체 컴파일러 에러: 에러 페이지 전체 목록이에요.
이해도 점검
섹션 제목: “이해도 점검”이해도 점검
1. Self를 타입으로 쓸 수 있는 곳은 어디일까요?
2. 최상위에 쓴 fn make_user() -> Self가 E0411을 내요. 보통 어떻게 고칠까요?
3. impl Counter 안에서 Self { count: 0 }은 무엇을 만들까요?
점수: 0 / 3
더 알아보기
섹션 제목: “더 알아보기”- E0411 — Rust error codes index (영어):
Self가 무엇을 뜻하는지, 왜 impl, 트레이트, 타입 정의 안에서만 통하는지 설명하는 공식 페이지예요. - 연관 함수 — The Rust Programming Language 한국어판:
fn square(size: u32) -> Self같은 연관 함수와,impl안에서Self가 동작하는 방식이에요. - The
Selfkeyword — standard library docs (영어): impl, 트레이트, 타입 정의에서Self가 뜻하는 것이에요. Selfin paths — The Rust Reference (영어): 중첩된 항목이 바깥의Self를 쓸 수 없는 이유까지 담은 정확한 규칙이에요.