Erro E0596 do Rust: cannot borrow as mutable, as it is not declared as mutable
E0596 significa que algum trecho de código quer mudar um valor (ele precisa de um empréstimo mutável, &mut), mas você só deu permissão para ler. Normalmente você esqueceu o mut em um let, ou uma função recebe & quando precisava de &mut. É um erro muito comum, e a linha help: do compilador quase sempre diz exatamente onde colocar o mut. Esta página mostra a correção primeiro e depois explica. Para ver os outros erros desta série, acesse Erros do compilador do Rust.
A mensagem de erro
Seção intitulada “A mensagem de erro”error[E0596]: cannot borrow `tasks` as mutable, as it is not declared as mutable --> src/main.rs:3:5 |3 | tasks.push("write tests"); // ❌ push precisa mudar `tasks` | ^^^^^ cannot borrow as mutable |help: consider changing this to be mutable |2 | let mut tasks = Vec::new(); | +++O compilador diz que não pode pegar tasks emprestado como mutável porque ela não foi declarada como mutável, e sugere adicionar mut. Este é o programa que causa o erro:
fn main() { let tasks = Vec::new(); tasks.push("write tests"); // ❌ push precisa mudar `tasks` println!("{tasks:?}");}Você também vai ver uma segunda versão da mensagem: “cannot borrow *tasks as mutable, as it is behind a & reference” (não é possível pegar emprestado como mutável, porque está atrás de uma referência &). É o mesmo código de erro, com uma causa um pouco diferente: ali a variável é uma referência &, então você não pode mudar aquilo para o qual ela aponta. As duas situações estão explicadas abaixo.
O que significa
Seção intitulada “O que significa”Olhe o ^^^^^ embaixo de tasks na linha 3. Para adicionar um item, Vec::push precisa de um empréstimo mutável do vetor: a assinatura dele é fn push(&mut self, value: T). Mas tasks foi criada com um let simples, então ela é só leitura. O Rust não pode entregar um &mut para algo que você disse que nunca mudaria.
A linha help: mostra a correção: let mut tasks. Os +++ marcam os três caracteres que você precisa adicionar.
Pense em um documento compartilhado com acesso “somente leitura”. Você pode ler cada palavra, mas o botão “editar” está desativado. mut (ou &mut) é o jeito de pedir acesso de edição.
Por que o Rust faz isso
Seção intitulada “Por que o Rust faz isso”No Rust, as variáveis são imutáveis por padrão, e as referências são só leitura por padrão. Para mudar qualquer coisa, você precisa pedir explicitamente: let mut para uma variável que é sua, &mut para uma referência a algo que você pegou emprestado. Veja Variáveis e Borrowing.
Isso deixa o código mais fácil de confiar. Se uma função recebe &Vec<String>, você sabe só pela assinatura que ela não vai mudar sua lista. Se ela recebe &mut Vec<String>, você sabe que pode mudar. Nada muda pelas suas costas.
Essa mesma regra torna possíveis as garantias de segurança maiores do Rust. Como “pode mudar” sempre está escrito no código, o compilador consegue garantir que nunca existe alguém escrevendo e alguém lendo ao mesmo tempo (isso é o E0502 e o E0499). O E0596 é o primeiro passo: você precisa pedir para escrever antes que alguém verifique se escrever é seguro.
Como corrigir
Seção intitulada “Como corrigir”Correção 1: Adicione mut à variável
Seção intitulada “Correção 1: Adicione mut à variável”Escolha esta quando o valor é seu e ele deve mudar: um Vec em que você faz push, uma String à qual você acrescenta texto, ou uma struct em que você chama um método &mut self. É a correção certa na maioria das vezes.
struct Counter { count: u32,}
impl Counter { fn increment(&mut self) { self.count += 1; }}
fn main() { let mut tasks = Vec::new(); tasks.push("write tests"); tasks.push("fix bug"); println!("{tasks:?}");
let mut counter = Counter { count: 0 }; counter.increment(); counter.increment(); println!("Contagem: {}", counter.count);}Saída:
["write tests", "fix bug"]Contagem: 2Sem mut em counter, counter.increment() falha com cannot borrow counter as mutable. Chamar um método que recebe &mut self pega a variável inteira emprestada como mutável, então a variável precisa ser mut. Métodos que recebem só &self funcionam sem ele. Veja Adicionando comportamento com métodos.
O mesmo vale para um parâmetro que pertence à função: fn shout(s: String) não pode chamar s.push('!'), mas fn shout(mut s: String) pode.
Correção 2: Mude o parâmetro para &mut e passe &mut x
Seção intitulada “Correção 2: Mude o parâmetro para &mut e passe &mut x”Escolha esta quando uma função precisa mudar algo que pertence a quem a chamou. Esta versão dá a mensagem com “behind a & reference”:
fn add_task(tasks: &Vec<String>, task: &str) { tasks.push(task.to_string()); // ❌ `tasks` é uma referência `&`}error[E0596]: cannot borrow `*tasks` as mutable, as it is behind a `&` reference --> src/main.rs:2:5 |2 | tasks.push(task.to_string()); // ❌ `tasks` é uma referência `&` | ^^^^^ `tasks` is a `&` reference, so it cannot be borrowed as mutable |help: consider changing this to be a mutable reference |1 | fn add_task(tasks: &mut Vec<String>, task: &str) { | +++Aqui o compilador explica que tasks é uma referência & e por isso não pode ser pega emprestada como mutável. Corrija em três lugares: o tipo do parâmetro vira &mut Vec<String>, a variável de quem chama vira let mut, e a chamada passa &mut tasks:
fn add_task(tasks: &mut Vec<String>, task: &str) { tasks.push(task.to_string());}
fn main() { let mut tasks = Vec::new(); add_task(&mut tasks, "write tests"); add_task(&mut tasks, "update docs"); println!("{} tarefas: {tasks:?}", tasks.len());}Saída:
2 tarefas: ["write tests", "update docs"]Se você mudar o parâmetro mas continuar passando &tasks, recebe o E0308 no lugar, com a nota types differ in mutability (os tipos diferem na mutabilidade). Isso só quer dizer: escreva &mut tasks na chamada também. Escrever &mut na chamada é de propósito; quem lê main vê na hora que add_task pode mudar tasks.
Correção 3: Torne mut a variável da closure
Seção intitulada “Correção 3: Torne mut a variável da closure”Escolha esta quando uma closure (função anônima) muda uma variável que ela capturou. Uma closure assim muda o próprio estado cada vez que roda, então chamá-la exige um empréstimo mutável da própria closure:
fn main() { let mut clicks = 0; let click = || clicks += 1; click(); // ❌ cannot borrow `click` as mutable click(); println!("Clicks: {clicks}");}error[E0596]: cannot borrow `click` as mutable, as it is not declared as mutable --> src/main.rs:3:9 |3 | let click = || clicks += 1; | ^^^^^ ------ calling `click` requires mutable binding due to mutable borrow of `clicks` | | | not mutable4 | click(); // ❌ cannot borrow `click` as mutable | ----- cannot borrow as mutable5 | click(); | ----- cannot borrow as mutable |help: consider changing this to be mutable |3 | let mut click = || clicks += 1; | +++A mensagem diz que chamar click exige uma variável mutável, porque a closure pega clicks emprestado como mutável. A variável clicks já é mut. O que falta é o mut na closure:
fn main() { let mut clicks = 0; let mut click = || clicks += 1; click(); click(); click(); println!("Cliques: {clicks}");}Saída:
Cliques: 3Isso surpreende muita gente, porque a closure não “parece” mudar. Mas ela guarda um &mut clicks dentro dela, então é uma closure FnMut, e chamar uma FnMut exige mut. Veja Closures que alteram variáveis capturadas e Fn, FnMut e FnOnce.
Correção 4: Use iter_mut() ou &mut no laço
Seção intitulada “Correção 4: Use iter_mut() ou &mut no laço”Escolha esta quando você percorre uma coleção e quer mudar cada item. .iter() e for x in &list entregam referências &, que são só leitura:
for name in names.iter() { name.push('!'); // ❌ cannot borrow `*name` as mutable, as it is behind a `&` reference}Troque por .iter_mut() ou &mut names (e torne names mutável também):
fn main() { let mut names = vec![String::from("ferris"), String::from("corro")]; for name in names.iter_mut() { name.push('!'); } println!("{names:?}");
let mut prices = vec![10, 20, 30]; for price in &mut prices { *price += 5; } println!("{prices:?}");}Saída:
["ferris!", "corro!"][15, 25, 35]O compilador também sugere isso: help: use mutable method ... names.iter_mut() (use o método mutável). O mesmo vale para closures em cadeias de iteradores: words.iter().for_each(|w| w.push('!')) falha, words.iter_mut().for_each(|w| w.push('!')) funciona. Veja Iteradores.
Onde você costuma ver esse erro
Seção intitulada “Onde você costuma ver esse erro”Montando um Vec ou uma String aos poucos. let list = Vec::new(); e depois list.push(...), ou let s = String::new(); e depois s.push_str(...). Correção: let mut. Se você já tem todos os itens de cara, let list = vec![a, b, c]; não precisa de mut nenhum.
Chamando um método “setter” ou de atualização em uma struct. account.deposit(50), em que deposit recebe &mut self, falha se você não escreveu let mut account. Correção: let mut account = Account::new();. Veja Structs.
Funções auxiliares que recebem & por costume. Você escreve fn reset(board: &Board) e depois tenta mudar board lá dentro. Correção: troque para board: &mut Board e chame reset(&mut board). Um bom hábito: escreva & quando a função só lê, e &mut quando ela muda.
Mudando um valor dentro de um HashMap. if let Some(v) = map.get("key") { v.push(..) } falha, porque get devolve uma referência &. Correção: use map.get_mut("key") ou a API entry. Veja HashMaps.
Erros relacionados
Seção intitulada “Erros relacionados”- E0384: cannot assign twice to immutable variable (não é possível atribuir duas vezes a uma variável imutável). O mesmo
mutesquecido, mas parax = ...oux += 1em vez de uma chamada de método. - E0594: não é possível atribuir a um campo ou através de uma referência
&, por exemplop.hp = 5;quandopnão émut, ou*x += 1emfor x in &v. As correções são as mesmas:let mutou&mut. - E0308: mismatched types (tipos incompatíveis),
types differ in mutability, quando você passa&xpara uma função que quer&mut x. - E0502: você conseguiu acesso
&mut, mas outra parte do código ainda está lendo o valor. - E0499: dois empréstimos
&mutdo mesmo valor ao mesmo tempo. - 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. let scores = Vec::new(); scores.push(10); dá E0596. Qual é a correção?
2. Uma função fn add(list: &Vec<u32>) chama list.push(1). O que você muda?
3. let mut total = 0; let add = |x| total += x; add(5); falha. Por quê?
Pontuação: 0 / 3
Para saber mais
Seção intitulada “Para saber mais”- E0596 — Rust error codes index (em inglês): a explicação oficial deste erro.
- References and Borrowing — The Rust Book (em inglês): a parte “Mutable References” explica o
&mute por que é preciso pedir por ele. - Method Syntax — The Rust Book (em inglês):
&selfversus&mut self, e o que cada um exige de quem chama. - Closures — The Rust Book (em inglês): como as closures capturam variáveis, e por que uma closure que muda algo precisa ser
mut. - Mutability (borrowing) — Rust by Example (em inglês): exemplos executáveis de empréstimos
&e&mutlado a lado.