Erro E0434 do Rust: can't capture dynamic environment in a fn item (fn interna)
O E0434 quer dizer que uma função escrita dentro de outra função tentou usar uma das variáveis locais da função de fora. Parece que deveria funcionar: a variável está logo ali, algumas linhas acima. Mas em Rust um fn aninhado é um item separado, e ele não enxerga as variáveis ao redor. A expressão do compilador, “dynamic environment” (ambiente dinâmico), quer dizer apenas “as variáveis locais que existem enquanto a função de fora está rodando”. Este é um dos erros mais fáceis de corrigir: o rustc te dá a resposta na linha help:. Esta página faz parte do guia de erros do compilador.
A mensagem de erro
Seção intitulada “A mensagem de erro”error[E0434]: can't capture dynamic environment in a fn item --> src/main.rs:5:24 |5 | price * (1.0 + tax_rate) // ❌ inner `fn` can't see `tax_rate` | ^^^^^^^^ | = help: use the `|| { ... }` closure form insteadEm português: “não é possível capturar o ambiente dinâmico num item fn”. A ajuda sugere usar a forma de closure, || { ... }.
Este é o programa que gera o erro:
fn main() { let tax_rate = 0.2;
fn with_tax(price: f64) -> f64 { price * (1.0 + tax_rate) // ❌ inner `fn` can't see `tax_rate` }
println!("{:.2}", with_tax(10.0));}(O comentário quer dizer “o fn interno não enxerga tax_rate”. Ele fica em inglês porque aparece na saída do compilador.)
O que significa
Seção intitulada “O que significa”O ^^^ aponta para tax_rate na linha 5. Essa é uma variável local de main, criada com let na linha 2. A função interna with_tax tenta ler essa variável, e isso não é permitido.
A linha help: dá a correção: escreva uma closure (função anônima), || { ... }, no lugar de um fn aninhado. Uma closure pode usar as variáveis ao redor. Um fn não pode, não importa onde você o escreva.
Muitas vezes você também recebe um aviso, unused variable: `tax_rate` (variável não usada), porque, do ponto de vista do Rust, nada poderia usá-la legalmente. Esse aviso some junto com o erro.
É como uma carta lacrada que você escreveu na sua mesa. Não importa que as suas anotações estavam na mesa enquanto você escrevia; quem abre a carta só vê o que você colocou dentro do envelope.
Por que o Rust faz isso
Seção intitulada “Por que o Rust faz isso”Um fn aninhado é compilado exatamente como um que está no topo do arquivo. Escrevê-lo dentro de main só esconde o nome dele do resto do código. Isso não o liga às variáveis de main, e há um bom motivo: uma função comum não tem onde guardá-las. Você pode chamar with_tax de qualquer lugar onde main a alcança, guardá-la como ponteiro de função ou chamá-la depois que as variáveis de main já sumiram. Um fn comum é só código, sem dados junto.
Uma closure é código mais as variáveis que ela capturou. O Rust guarda esses valores (ou referências a eles) dentro da closure, e o borrow checker garante que eles vivam tempo suficiente. É por isso que o rustc te manda para as closures. Veja Capturando variáveis do escopo ao redor.
A mesma regra vale para parâmetros genéricos: um fn interno também não pode usar o T da função de fora. Esse é o E0401.
Como corrigir
Seção intitulada “Como corrigir”Correção 1: Use uma closure
Seção intitulada “Correção 1: Use uma closure”Escolha esta na maioria das vezes, quando a função auxiliar é pequena e vive dentro de uma função só. Escreva let nome = |parâmetros| corpo; e a closure pode usar qualquer variável do escopo:
fn main() { let tax_rate = 0.2;
let with_tax = |price: f64| price * (1.0 + tax_rate); // ✅ uma closure enxerga `tax_rate`
println!("{:.2}", with_tax(10.0)); println!("{:.2}", with_tax(24.5));
let prices = [3.0, 7.5, 12.0]; let total: f64 = prices.iter().map(|&p| with_tax(p)).sum(); println!("total: {:.2}", total);}Saída:
12.0029.40total: 27.00Uma closure que só lê uma variável pega ela emprestada, então você ainda pode usar tax_rate depois. Se você mudar a variável dentro da closure, a closure precisa de let mut. Veja Closures.
Correção 2: Passe o valor como argumento
Seção intitulada “Correção 2: Passe o valor como argumento”Escolha esta quando a função auxiliar deve continuar sendo uma função de verdade: ela é mais longa, você quer testá-la ou várias funções a usam. Dê a ela mais um parâmetro e passe o valor:
fn with_tax(price: f64, tax_rate: f64) -> f64 { price * (1.0 + tax_rate) // ✅ agora `tax_rate` é um parâmetro}
fn receipt(prices: &[f64], tax_rate: f64) -> f64 { prices.iter().map(|&p| with_tax(p, tax_rate)).sum()}
fn main() { let tax_rate = 0.2; println!("{:.2}", with_tax(10.0, tax_rate)); println!("total: {:.2}", receipt(&[3.0, 7.5, 12.0], tax_rate));
let reduced_rate = 0.05; println!("{:.2}", with_tax(10.0, reduced_rate));}Saída:
12.00total: 27.0010.50Aqui a função foi para o nível de cima do arquivo, que é onde vive a maioria das funções auxiliares em Rust. Também funciona se você a mantiver aninhada; o que importa é que o valor entra como argumento. De bônus, agora ela funciona com qualquer alíquota, não só com uma. Veja Parâmetros.
Correção 3: Transforme em const
Seção intitulada “Correção 3: Transforme em const”Escolha esta quando o valor é fixo e conhecido antes de o programa rodar, como uma alíquota de imposto escrita no código. Diferente de uma variável let, um const é um item, e qualquer função pode usá-lo, aninhada ou não:
const TAX_RATE: f64 = 0.2; // ✅ uma constante, visível para todas as funções
fn main() { fn with_tax(price: f64) -> f64 { price * (1.0 + TAX_RATE) }
println!("{:.2}", with_tax(10.0)); println!("{:.2}", with_tax(24.5));}Saída:
12.0029.40Isso só funciona para valores que podem ser calculados em tempo de compilação. Se a alíquota vem de um arquivo, da entrada do usuário ou de outra variável, um const não serve: const RATE: f64 = tax_rate; dá outro erro, o E0435 (attempt to use a non-constant value in a constant, tentativa de usar um valor não constante numa constante). Nesse caso, use a Correção 1 ou a 2. Veja Constantes.
Onde você costuma ver esse erro
Seção intitulada “Onde você costuma ver esse erro”Quem vem do JavaScript ou do Python. Nessas linguagens, uma função aninhada enxerga as variáveis de fora, então fn dentro de fn parece natural. Em Rust, quem faz isso é a closure. Correção: let helper = |x| ...; no lugar de fn helper(x) ....
Usar self num fn aninhado dentro de um método. fn report(&self) { fn show() { println!("{}", self.n); } } dá o mesmo E0434, apontando para self. Correção: transforme show num método (fn show(&self) no impl) e chame self.show(), ou use uma closure.
Um parâmetro da função, não um let. Em fn total(prices: &[f64], discount: f64), um fn apply(price: f64) -> f64 { price - discount } aninhado também dá E0434: os parâmetros também são variáveis locais. Correção: passe discount para apply, ou escreva let apply = |price: f64| price - discount;.
Erros relacionados
Seção intitulada “Erros relacionados”- E0401: can’t use generic parameters from outer item: a mesma regra para tipos: um
fninterno não pode usar oTnem oSelfde fora. - E0425: cannot find value in this scope: o nome nem existe, por exemplo por um erro de digitação ou por ser uma variável de outra função.
- E0373: closure may outlive the current function: o que pode aparecer depois de trocar para uma closure, se ela pega uma variável emprestada e vive mais que ela (correção:
move). - E0424:
selfis not available here:selfusado numa função que não é método. - E0435: um
constque tenta usar uma variávellet, mostrado na Correção 3. - Todos os erros do compilador
Teste o que aprendeu
Seção intitulada “Teste o que aprendeu”Teste o que aprendeu
1. Dentro de main, você tem let limit = 10; e um fn check(n: i32) -> bool { n < limit } aninhado. O que acontece?
2. Qual versão pode usar a variável local limit?
3. O valor vem da entrada do usuário, enquanto o programa roda. Qual correção NÃO vai funcionar?
Pontuação: 0 / 3
Para saber mais
Seção intitulada “Para saber mais”- E0434 — Rust error codes index (em inglês): a explicação oficial, com a correção por closure e a correção com
const/static. - Closures — The Rust Book (em inglês): como as closures capturam o ambiente, algo que um
fnaninhado não consegue fazer. - Capturing — Rust by Example (em inglês): empréstimo, empréstimo mutável e mover valores para dentro de uma closure, com exemplos pequenos.
- Item declarations — The Rust Reference (em inglês): a regra de que um item dentro de uma função não pode usar os parâmetros nem as variáveis locais dela.
- Constants — The Rust Book (em inglês): como
constdifere delet, e o que uma expressão constante pode conter.