Cell în Rust

Celulă cu mutabilitate interioară pentru valori Copy: get() copiază valoarea afară, set() pune una nouă, prin &self.

Sintaxă
use std::cell::Cell;

fn new(value: T) -> Cell<T>
fn get(&self) -> T      // T: Copy
fn set(&self, val: T)

Ce face Cell

Cell<T> permite schimbarea valorii prin referință comună, ca și RefCell, dar fără împrumuturi: nu primești niciodată o referință spre interior. get() returnează o copie a valorii (deci T trebuie să fie Copy), iar set(v) înlocuiește valoarea.

Pentru că nu există împrumuturi, Cell nu poate intra în panică și nu are cost la rulare. Este potrivit pentru contoare, indicatori bool sau valori mici într-o structură folosită prin &self. Pentru tipuri care nu sunt Copy există take() și replace(). Ca și RefCell, nu poate fi împărțit între fire.

Parametri

Parametru Descriere
value Valoarea inițială (la new).
val Noua valoare (la set).

Ce returnează

get: o copie a valorii. set: nimic.

Exemple

Exemplul 1: Contor de vizite prin &self

use std::cell::Cell;

struct Pagina {
    titlu: &'static str,
    vizite: Cell<u32>,
}

impl Pagina {
    fn deschide(&self) {
        self.vizite.set(self.vizite.get() + 1);
    }
}

fn main() {
    let p = Pagina { titlu: "Option în Rust", vizite: Cell::new(0) };
    for _ in 0..3 {
        p.deschide();
    }
    println!("{}: {} vizite", p.titlu, p.vizite.get());
}

Rezultat

Option în Rust: 3 vizite

Exemplul 2: Indicator comun pentru mai multe closure-uri

Ambele closure-uri împrumută celula prin &, lucru care nu ar fi posibil cu un bool obișnuit modificat în ele.

use std::cell::Cell;

fn main() {
    let salvat = Cell::new(true);
    let modifica = || salvat.set(false);
    let salveaza = || salvat.set(true);

    println!("la început: {}", salvat.get());
    modifica();
    println!("după modificare: {}", salvat.get());
    salveaza();
    println!("după salvare: {}", salvat.get());

    let nume = Cell::new(String::from("Ana"));
    let vechi = nume.replace(String::from("Ioana"));
    println!("vechi = {vechi}, nou = {}", nume.take());
}

Rezultat

la început: true
după modificare: false
după salvare: true
vechi = Ana, nou = Ioana

Greșeli frecvente

  • Apelezi get() pe un Cell<String>: nu compilează, pentru că String nu este Copy. Folosește take()/replace() sau RefCell.
  • Folosești Cell între fire: nu este Sync. Pentru un contor comun folosește AtomicUsize.

Comenzi înrudite

  • RefCell: Permite modificarea unei valori prin referință comună (&), verificând regulile de împrumut la rulare, nu la compilare.
  • AtomicUsize: Număr întreg care poate fi citit și modificat din mai multe fire fără Mutex, cu operații atomice.
  • mem::replace: Pune o valoare nouă într-un loc mutabil și returnează valoarea veche, mutată afară.