Mutabilidade Interior
Em algumas situações, é necessário modificar dados atrás de uma referência compartilhada (somente leitura). Por exemplo, uma estrutura de dados compartilhada pode ter um cache interno e desejar atualizar esse cache a partir de métodos somente leitura.
O padrão de “mutabilidade interna” permite acesso exclusivo (mutável) por trás de uma referência compartilhada. A biblioteca padrão fornece várias maneiras de fazer isso, garantindo segurança, geralmente realizando uma verificação em tempo de execução.
RefCell
use std::cell::RefCell; fn main() { // Observe que `cell` NÃO é declarado como mutável. let cell = RefCell::new(5); { let mut cell_ref = cell.borrow_mut(); *cell_ref = 123; // Isso gera um erro em tempo de execução. // let other = cell.borrow(); // println!("{}", *other); } println!("{cell:?}"); }
Cell
Cell
envolve um valor e permite obter ou definir o valor, mesmo com uma referência compartilhada para o Cell
. No entanto, não permite nenhuma referência ao valor. Como não há referências, as regras de empréstimo não podem ser quebradas.
use std::cell::Cell; fn main() { // Observe que `cell` NÃO é declarado como mutável. let cell = Cell::new(5); cell.set(123); println!("{}", cell.get()); }
O principal a ser observado neste slide é que o Rust fornece maneiras seguras de modificar dados por trás de uma referência compartilhada. Há uma variedade de maneiras de garantir essa segurança, e RefCell
e Cell
são duas delas.
-
RefCell
faz cumprir as regras de empréstimo usuais do Rust (ou várias referências compartilhadas ou uma única referência exclusiva) com uma verificação em tempo de execução. Neste caso, todos os empréstimos são muito curtos e nunca se sobrepõem, então as verificações sempre têm sucesso.- O bloco extra no exemplo de
RefCell
é para encerrar o empréstimo criado pela chamada aborrow_mut
antes de imprimir ocell
. Tentar imprimir umRefCell
emprestado mostra apenas a mensagem"{borrowed}"
.
- O bloco extra no exemplo de
-
Cell
é um meio mais simples de garantir a segurança: ele tem um métodoset
que recebe&self
. Isso não precisa de uma verificação em tempo de execução, mas requer mover valores, o que pode ter seu próprio custo. -
Tanto
RefCell
quantoCell
são!Sync
, o que significa que&RefCell
e&Cell
não podem ser passados entre threads. Isso impede que duas threads tentem acessar ocell
ao mesmo tempo.