Erro E0401 do Rust: can't use generic parameters from outer item (fn interna)
O E0401 quer dizer que uma função escrita dentro de outra função tentou usar o tipo genérico da função de fora, como T, ou o Self do impl em que ela está. Parece que deveria funcionar, porque a função interna está ali, dentro da de fora. Mas em Rust uma fn aninhada é um item separado, que não enxerga o T de fora. É uma surpresa comum quando você organiza uma função genérica puxando uma função auxiliar para dentro dela, e a correção leva um minuto. Esta página faz parte do guia de erros do compilador.
A mensagem de erro
Seção intitulada “A mensagem de erro”error[E0401]: can't use generic parameters from outer item --> src/main.rs:2:20 |1 | fn print_all<T: std::fmt::Display>(items: &[T]) { | - type parameter from outer item2 | fn show(item: &T) { // ❌ inner `fn` can't see the outer `T` | ---- ^ use of generic parameter from outer item | | | generic parameter used in this inner function | = note: nested items are independent from their parent item for everything except for privacy and name resolutionhelp: try introducing a local generic parameter here |2 | fn show<T>(item: &T) { // ❌ inner `fn` can't see the outer `T` | +++Em português: “não é possível usar parâmetros genéricos do item de fora”. A ajuda sugere declarar um parâmetro genérico local: fn show<T>.
Este é o programa que gera o erro:
fn print_all<T: std::fmt::Display>(items: &[T]) { fn show(item: &T) { // ❌ inner `fn` can't see the outer `T` println!("- {}", item); }
for item in items { show(item); }}
fn main() { print_all(&["apples", "pears"]);}(O comentário quer dizer “a fn interna não enxerga o T de fora”. Ele fica em inglês porque aparece na saída do compilador.)
O que significa
Seção intitulada “O que significa”O rustc aponta para três lugares:
- O
-embaixo doTna linha 1: “type parameter from outer item” (parâmetro de tipo do item de fora). Este é oTque pertence aprint_all. - O
^embaixo doTna linha 2: “use of generic parameter from outer item” (uso de um parâmetro genérico do item de fora). A função internashowtenta usar esseT. - O
----embaixo deshow: “generic parameter used in this inner function” (parâmetro genérico usado nesta função interna). Esta é a função que não tem permissão para isso.
A note: resume a regra numa linha: itens aninhados são independentes do item pai, a não ser pela privacidade e pela resolução de nomes. Colocar fn show dentro de print_all só esconde o nome dela do resto do arquivo. Isso não dá a show acesso a nada que pertence a print_all: nem ao T, nem aos parâmetros, nem às variáveis locais.
É como um quarto de hóspedes numa casa. O quarto fica dentro da casa, mas o hóspede não ganha a chave do seu quarto só porque dorme no fim do corredor.
Por que o Rust faz isso
Seção intitulada “Por que o Rust faz isso”Uma função genérica como print_all<T> é um molde. O Rust gera uma cópia separada para cada T com que você a chama: uma para &str, uma para i32, e assim por diante. Já uma fn aninhada é compilada uma vez só, como um item próprio, exatamente como se você a tivesse escrito no topo do arquivo. Então, dentro de show, não existe um único T a que ela poderia se referir.
O Rust poderia ter feito as funções aninhadas se copiarem junto com a função pai, mas aí elas se comportariam de um jeito muito diferente de todas as outras fn. Em vez disso, a regra é simples: uma fn interna só sabe o que você passa para ela, pelos seus próprios parâmetros genéricos e argumentos. Se você quer algo que enxergue o código em volta, o Rust tem as closures para isso. Veja Genéricos e Closures.
Como corrigir
Seção intitulada “Como corrigir”Correção 1: Dê à função interna o seu próprio parâmetro genérico
Seção intitulada “Correção 1: Dê à função interna o seu próprio parâmetro genérico”Escolha esta na maioria das vezes. Declare <T> também na função interna, com os mesmos bounds (restrições) de que ela precisa. A ajuda do rustc sugere fn show<T>, mas se você acrescentar só o <T>, recebe um erro novo, o E0277 (`T` doesn't implement `std::fmt::Display`, T não implementa Display), porque o novo T ainda não tem bound nenhum. Copie os bounds:
use std::fmt::Display;
fn print_all<T: Display>(items: &[T]) { fn show<T: Display>(item: &T) { println!("- {}", item); }
for item in items { show(item); }}
fn main() { print_all(&["maçãs", "peras"]); print_all(&[3, 7]);}Saída:
- maçãs- peras- 3- 7O T de dentro é um parâmetro novo que por acaso tem o mesmo nome. Quando print_all chama show(item), o Rust preenche esse parâmetro com o T de fora. Você poderia chamá-lo de U para deixar isso mais claro.
Correção 2: Use uma closure
Seção intitulada “Correção 2: Use uma closure”Escolha esta quando a função auxiliar é pequena, ou quando ela também precisa das variáveis da função de fora. Uma closure não é um item separado, então ela enxerga tudo em volta: o T de fora, os parâmetros da função e as variáveis locais. Aqui a closure também usa label, que uma fn aninhada nunca conseguiria alcançar:
use std::fmt::Display;
fn print_all<T: Display>(label: &str, items: &[T]) { let show = |item: &T| println!("{}: {}", label, item);
for item in items { show(item); }}
fn main() { print_all("fruta", &["maçãs", "peras"]);}Saída:
fruta: maçãsfruta: perasAs closures não podem ser genéricas por conta própria, mas aqui não precisam ser: dentro de print_all<T>, &T já é um tipo concreto em cada cópia da função. Veja Closures.
Correção 3: Mova a função para fora (ou para dentro do impl)
Seção intitulada “Correção 3: Mova a função para fora (ou para dentro do impl)”Escolha esta quando a função auxiliar é útil por si só, ou quando o erro é sobre Self. Dentro de um método, uma fn aninhada também não pode usar Self, porque ela fica fora do impl do mesmo jeito:
error[E0401]: can't use `Self` from outer item --> src/main.rs:7:24 |5 | impl Inventory { | ---- `Self` type implicitly declared here, by this `impl`6 | fn report(&self) {7 | fn count(inv: &Self) -> usize { // ❌ inner `fn` can't see `Self` | ----- ^^^^ use of `Self` from outer item | | | `Self` used in this inner function | = note: nested items are independent from their parent item for everything except for privacy and name resolutionhelp: refer to the type directly here instead |7 - fn count(inv: &Self) -> usize { // ❌ inner `fn` can't see `Self`7 + fn count(inv: &Inventory) -> usize { // ❌ inner `fn` can't see `Self` |Aqui o rustc diz que não dá para usar o Self do item de fora, mostra que o Self foi declarado implicitamente por aquele impl, e sugere escrever o tipo diretamente: &Inventory.
O programa com erro:
struct Inventory { items: Vec<String>,}
impl Inventory { fn report(&self) { fn count(inv: &Self) -> usize { // ❌ inner `fn` can't see `Self` inv.items.len() } println!("{} items", count(self)); }}
fn main() { let inv = Inventory { items: vec!["rope".to_string()] }; inv.report();}A ajuda do rustc (escrever &Inventory) funciona. Mas normalmente a correção mais limpa é subir count um nível, para que ela vire um método próprio. Ali, Self e self funcionam normalmente:
struct Inventory { items: Vec<String>,}
impl Inventory { fn count(&self) -> usize { self.items.len() }
fn report(&self) { println!("{} itens", self.count()); }}
fn main() { let inv = Inventory { items: vec!["corda".to_string(), "lanterna".to_string()], }; inv.report();}Saída:
2 itensPara uma função auxiliar genérica como show, “mover para fora” quer dizer uma fn show<T: Display>(item: &T) no nível de cima do arquivo, que outras funções também podem reaproveitar. Veja Structs e Funções em Rust.
Onde você costuma ver esse erro
Seção intitulada “Onde você costuma ver esse erro”Uma struct ou um enum dentro de uma função genérica. fn group<T>(items: Vec<T>) com struct Pair { a: T, b: T } lá dentro dá E0401 em cada T. Correção: deixe o tipo interno genérico também, struct Pair<T> { a: T, b: T }, ou mova-o para o nível de cima.
Um const que depende de T. const SIZE: usize = std::mem::size_of::<T>(); dentro de uma função genérica também é E0401, porque um const também é um item. Correção: use um let: let size = std::mem::size_of::<T>();.
Uma função interna que usa uma variável local. É a mesma ideia, só que com um valor em vez de um tipo, e com outro código: E0434 (can't capture dynamic environment in a fn item, não é possível capturar o ambiente dinâmico num item fn). Correção: passe o valor como argumento, ou use uma closure (Correção 2).
Erros relacionados
Seção intitulada “Erros relacionados”- E0411:
Selfusado fora de qualquerimplou trait, por exemplo numafn new() -> Selfno nível de cima. - E0277: o trait bound não é satisfeito, que é o que você recebe depois de
fn show<T>sem copiar oT: Display. - E0425: um valor ou tipo não encontrado, por exemplo um tipo que você nunca declarou.
- E0107: número errado de argumentos genéricos, outro erro com genéricos.
- E0434: uma
fninterna usa uma variável local da função de fora, mostrado acima. - Todos os erros do compilador: a lista completa de páginas de erro.
Teste o que aprendeu
Seção intitulada “Teste o que aprendeu”Teste o que aprendeu
1. Dentro de fn print_all<T>(...), por que uma fn show(item: &T) aninhada não pode usar T?
2. Qual função auxiliar dentro de fn print_all<T: Display> pode usar o T de fora e as variáveis locais de fora?
3. Você segue a ajuda do rustc e escreve fn show<T>(item: &T), e depois imprime item com {}. O que acontece?
Pontuação: 0 / 3
Para saber mais
Seção intitulada “Para saber mais”- E0401 — Rust error codes index (em inglês): a explicação oficial, com as três correções: um parâmetro genérico local, uma closure ou mover a função para fora.
- Item declarations — The Rust Reference (em inglês): a regra de que um item interno não captura os parâmetros genéricos, os parâmetros nem as variáveis locais da função de fora.
- Generic Data Types — The Rust Book (em inglês): como
<T>e os trait bounds funcionam em funções, structs e métodos. - Closures — The Rust Book (em inglês): como as closures capturam o ambiente em volta, coisa que funções aninhadas não conseguem fazer.