Pular para o conteúdo

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.

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.

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.

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.

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: 2
▶ Experimente você mesmo

Sem 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"]
▶ Experimente você mesmo

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.

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 mutable
4 | click(); // ❌ cannot borrow `click` as mutable
| ----- cannot borrow as mutable
5 | 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: 3
▶ Experimente você mesmo

Isso 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.

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]
▶ Experimente você mesmo

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.

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.

  • E0384: cannot assign twice to immutable variable (não é possível atribuir duas vezes a uma variável imutável). O mesmo mut esquecido, mas para x = ... ou x += 1 em vez de uma chamada de método.
  • E0594: não é possível atribuir a um campo ou através de uma referência &, por exemplo p.hp = 5; quando p não é mut, ou *x += 1 em for x in &v. As correções são as mesmas: let mut ou &mut.
  • E0308: mismatched types (tipos incompatíveis), types differ in mutability, quando você passa &x para 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 &mut do mesmo valor ao mesmo tempo.
  • Todos os erros do compilador: a lista completa de páginas de erro.

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