W Rust struktury danych są domyślnie niezmienne. Oznacza to, że każda zmienna lub odniesienie zadeklarowane bez słowa kluczowego mut nie może być zmieniane po inicjalizacji.
let mut value = 10; // zmienna zmienna value += 5; let value2 = 10; // zmienna niezmienna // value2 += 5; // błąd kompilacji: cannot assign twice to immutable variable
Ta sama zasada odnosi się do pól struktur:
struct Point { x: i32, y: i32 } let mut pt = Point { x: 1, y: 2 }; pt.x = 5; // OK, ponieważ pt jest zadeklarowane jako mut
Jednak zmienność dotyczy tylko "najwyższego poziomu" zmiennej: jeśli struktura jest przechowywana za pomocą niezmiennego odniesienia, jej dane nie mogą być zmieniane, nawet jeśli są zadeklarowane jako mut wewnątrz struktury.
Innym sposobem na obejście ograniczeń jest użycie specjalnych typów, takich jak RefCell<T> lub kontenery atomowe. Pozwala to na modyfikowanie danych wewnątrz wyglądających na niezmienne kontenerów przy użyciu "wewnętrznej zmienności" (interior mutability), na przykład:
use std::cell::RefCell; struct Counter { count: RefCell<i32>, } let counter = Counter { count: RefCell::new(0) }; *counter.count.borrow_mut() += 1; // bezpieczne, mimo braku mut
Pytanie: Czy można zmienić wartość pola struktury, jeśli sama zmienna nie jest zadeklarowana jako mut, ale pole jest jawnie zadeklarowane jako mut w samej strukturze?
Typowa nieprawidłowa odpowiedź: Tak, jeśli pole jest zadeklarowane jako mut, można je zmieniać.
Prawidłowa odpowiedź: Słowo kluczowe mut nie może być używane przy deklaracji pola struktury. Zmienność odnosi się tylko do samej zmiennej lub do uzyskanego odniesienia. Aby zmodyfikować pole struktury, należy albo zadeklarować zmienną jako mut, albo użyć typów "wewnętrznej zmienności" (na przykład, RefCell).
Przykład:
struct Foo { val: i32 } // pole zadeklarowane bez mut let mut foo = Foo { val: 1 }; foo.val = 2; // OK let foo2 = Foo { val: 3 }; foo2.val = 4; // błąd! zmienna nie jest mutable
Historia
W dużym projekcie jeden z programistów próbował zmienić pole struktury, sądząc, że zadeklarowanie pola z użyciem mut ("struct S { mut x: i32 }") da potrzebną zmienność. To nie mogło się skompilować, a proces refaktoryzacji przeciągnął się na kilka godzin, zanim wyjaśniono mu, że zmienność to cecha własności zmiennej, a nie struktury.
Historia
W projekcie związanym z wielowątkowością, cała logika kontroli dostępu do danych była przechowywana w globalnej statycznej strukturze. Nie znając możliwości "wewnętrznej zmienności" przez
RefCell, zespół pisał nadmiernie skomplikowane mechanizmy blokad i wyścigów, zamiast prostego rozwiązania z użyciemArc<Mutex<T>>lubRwLock<T>.
Historia
Programista zapomniał zadeklarować argument funkcji mut wewnątrz sygnatury, a funkcja nie mogła zmienić przekazanej struktury, przez co błąd ujawnił się dopiero w produkcji (dane nie były aktualizowane po wywołaniu funkcji). Wiedza, że domyślnie przekazywanie odbywa się przez niezmienne odniesienie, pozwoliłaby mu uniknąć błędu na wczesnym etapie.