콘텐츠로 이동

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번째 줄에서 두 곳을 가리켜요.

  1. Self 아래의 ^^^^: “Self is only available in impls, traits, and type definitions”(Self는 impl, 트레이트, 타입 정의 안에서만 쓸 수 있다). 규칙을 한 줄로 말해 줘요.
  2. new_counter 아래의 -----: “Self not allowed in a function”(함수에서는 Self를 쓸 수 없다). 여기서의 이유예요. new_counter는 어느 impl에도 속하지 않은 평범한 함수예요.

방금 Counter { count: 0 }이라고 썼으니 new_counter가 Counter를 돌려준다는 건 여러분은 알아요. 하지만 Rust는 Self의 뜻을 짐작하려고 함수 본문을 읽지 않아요. Self는 바깥을 감싸는 impl Counter { ... }나 trait ... { ... }에서만 뜻을 얻는데, 여기에는 그런 게 없어요.

포스트잇에 적힌 “여기”라는 말과 같아요. 지도 위의 “현재 위치: 여기”는 말이 되지만, 식탁 위에 놓인 쪽지의 “여기”가 어디인지는 아무도 몰라요.

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);
}

실행 결과:

2
▶ 직접 실행해 보기

impl 안에서는 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);
}

실행 결과:

10
0
▶ 직접 실행해 보기

impl 밖으로 복사해 낸 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