Pular para o conteúdo

Erro E0411 do Rust: cannot find type Self in this scope (Self fora de um impl)

O E0411 quer dizer que você escreveu Self num lugar onde o Rust não consegue saber o que ele representa. Self é um atalho para “o tipo de que este bloco impl (ou trait) trata”, então ele só funciona dentro de um impl, de um trait ou de uma definição de tipo. A causa de costume é um construtor como fn new() -> Self escrito no nível do arquivo, ou um método que você copiou para fora do seu bloco impl. É um erro comum e rápido de corrigir. Ao contrário do E0412, que virou E0425 no Rust 1.93, este código não mudou: o rustc 1.97 continua mostrando E0411. Esta página faz parte do guia de erros do compilador.

error[E0411]: cannot find type `Self` in this scope
--> src/main.rs:5:21
|
5 | fn new_counter() -> Self { // ❌ `Self` fora de um `impl`
| ----------- ^^^^ `Self` is only available in impls, traits, and type definitions
| |
| `Self` not allowed in a function

Em português: “não foi possível encontrar o tipo Self neste escopo”. O rustc explica que Self só existe em impls, traits e definições de tipo, e que ele não é permitido numa função.

Este é o programa que gera o erro:

struct Counter {
count: u32,
}
fn new_counter() -> Self { // ❌ `Self` fora de um `impl`
Counter { count: 0 }
}
fn main() {
let c = new_counter();
println!("{}", c.count);
}

O rustc aponta para duas coisas na linha 5:

  1. O ^^^^ embaixo de Self: “Self is only available in impls, traits, and type definitions” (Self só está disponível em impls, traits e definições de tipo). Essa é a regra, numa linha.
  2. O ----- embaixo de new_counter: “Self not allowed in a function” (Self não é permitido numa função). Este é o motivo aqui: new_counter é uma função comum, que não faz parte de nenhum impl.

Você pode até saber que new_counter devolve um Counter, porque acabou de escrever Counter { count: 0 }. Mas o Rust não lê o corpo da função para adivinhar o que Self significa. Self só ganha sentido a partir de um impl Counter { ... } ou trait ... { ... } em volta, e aqui não há nenhum.

É como a palavra “aqui” num post-it. Num mapa, “você está aqui” faz sentido. Num bilhete largado na mesa da cozinha, ninguém sabe onde é “aqui”.

Self não é um tipo por si só. Ele é um marcador que cada impl ou trait preenche: dentro de impl Counter, Self quer dizer Counter; dentro de impl Timer, quer dizer Timer; dentro de um trait, quer dizer “o tipo que implementar este trait, seja ele qual for”. É isso que o torna útil: você pode renomear a struct, ou copiar um método para outro impl, e o Self acompanha.

Uma função solta não pertence a nenhum tipo, então não há nada para preencher. O Rust até poderia tentar adivinhar pelo corpo, mas aí uma assinatura dependeria do código dentro dela, e o Rust verifica as assinaturas por si sós, de propósito. Veja em Structs o lugar de costume de um fn new() -> Self.

Correção 1: Mova a função para dentro de um bloco impl

Seção intitulada “Correção 1: Mova a função para dentro de um bloco impl”

Escolha esta na maioria das vezes. Uma função que cria um tipo ou trabalha com ele pertence ao bloco impl desse tipo, e lá o Self funciona do jeito que você esperava. Chame-a com 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);
}

Saída:

2
▶ Experimente você mesmo

Dentro do impl, você pode usar Self tanto como tipo (-> Self) quanto para criar um valor (Self { count: 0 }). Veja Structs.

Correção 2: Escreva o nome do tipo no lugar de Self

Seção intitulada “Correção 2: Escreva o nome do tipo no lugar de Self”

Escolha esta quando a função deve continuar sendo uma função solta, por exemplo uma função auxiliar que cria vários tipos, ou que mora em outro módulo. Basta escrever o tipo de verdade, Counter, onde você tinha escrito Self:

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

Saída:

10
0
▶ Experimente você mesmo

Esta também é a correção para um fn reset(c: &mut Self) copiado para fora de um impl: todo Self na assinatura e no corpo vira Counter.

Correção 3: Use um tipo genérico se você queria dizer “qualquer tipo”

Seção intitulada “Correção 3: Use um tipo genérico se você queria dizer “qualquer tipo””

Escolha esta quando você usou Self para dizer “o tipo que quem chama quiser”, que é trabalho para um T genérico, e não para Self. Declare <T> com os traits de que a função precisa. Aqui, T: Default deixa a função criar um valor inicial de qualquer tipo:

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

Saída:

(0, 0)
("", "")
▶ Experimente você mesmo

Se você quer que cada tipo decida como é criado, coloque a função num trait: trait Reset { fn fresh() -> Self; }. Dentro de um trait, Self quer dizer “o tipo que o implementa”. Veja Genéricos e Traits.

Self { ... } no main ou numa função solta. let c = Self { count: 0 }; dá E0411 com uma primeira linha um pouco diferente: cannot find struct, variant or union type `Self` in this scope (não foi encontrado o tipo struct, variante ou union Self neste escopo). Correção: escreva Counter { count: 0 }, ou chame Counter::new().

Uma função auxiliar dentro de um método. Dentro de impl Counter, um método que contém a sua própria fn helper() -> Self recebe outro código, o E0401: can't use `Self` from outer item (não dá para usar o Self do item de fora). Uma função dentro de outra função não herda o impl de fora. Correção: escreva Counter na função de dentro, ou use uma closure, ou leve helper para o impl como um método próprio.

Confundir Self com self. Self com maiúscula é o tipo; self com minúscula é o valor em que um método é chamado. Usar self fora de um método é outro erro, o E0424. A correção para os dois: confira se o código está dentro do bloco impl certo.

  • E0424: self (minúsculo, o valor) usado fora de um método.
  • E0425: não foi encontrado um valor, uma função ou (desde o Rust 1.93) um tipo com um nome comum.
  • E0412: o código antigo para “cannot find type”, hoje mostrado como E0425.
  • E0405: não foi encontrado um trait, normalmente por falta de uma linha use.
  • E0401: o Self de um impl de fora usado dentro de uma função aninhada, mostrado acima.
  • Todos os erros do compilador: a lista completa de páginas de erro.

Teste o que aprendeu

1. Onde você pode usar Self como tipo?

2. fn make_user() -> Self no nível do arquivo dá E0411. Qual é a correção de costume?

3. Dentro de impl Counter, o que Self { count: 0 } cria?

Pontuação: 0 / 3