Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

RefCell<T> ve İçsel Değiştirilebilirlik Deseni

İçsel değiştirilebilirlik (interior mutability), elde yalnızca değiştirilemez referanslar olsa bile veriyi değiştirmeye izin veren bir Rust tasarım desenidir. Normalde ödünç alma kuralları buna izin vermez. Bu desen, değişiklik ve ödünç alma kurallarını yöneten normal Rust davranışını esnetmek için veri yapısının içinde unsafe kod kullanır. Unsafe, kuralları derleyiciye değil bizim manuel denetlediğimizi söyler; ayrıntısını 20. bölümde göreceğiz.

Bu deseni kullanan türleri ancak ödünç alma kurallarının çalışma zamanında da izleneceğinden eminseniz kullanmalısınız. İçteki unsafe kod güvenli bir API arkasına saklanır; dışarıdan bakınca tür yine değiştirilemez görünür.

Bu fikri, içsel değiştirilebilirliği kullanan RefCell<T> türü üzerinden inceleyelim.

Ödünç Alma Kurallarını Çalışma Zamanında Uygulamak

Rc<T>den farklı olarak RefCell<T>, tuttuğu veri üzerinde tek sahipliği temsil eder. Peki onu Box<T>den ayıran nedir? 4. bölümdeki ödünç alma kurallarını hatırlayın:

  • Aynı anda ya tek bir değiştirilebilir referansınız olabilir ya da istediğiniz kadar değiştirilemez referansınız olabilir; ikisi bir arada olamaz.
  • Referanslar her zaman geçerli olmalıdır.

Referanslar ve Box<T> ile bu kuralların değişmezleri derleme zamanında uygulanır. RefCell<T> ileyse çalışma zamanında uygulanır. Referanslarla kuralları bozarsanız derleyici hata verir. RefCell<T> ile bozarsanız program panic! ile kapanır.

Derleme zamanında denetlemenin avantajı, hataların daha erken yakalanması ve çalışma zamanı maliyeti olmamasıdır. Bu yüzden Rust’ta varsayılan yaklaşım budur.

Çalışma zamanında denetlemenin avantajıysa, derleme zamanında fazla katı kalan denetimlerin reddedeceği bazı bellek-güvenli senaryoları mümkün kılmasıdır. Rust derleyicisi gibi durağan analiz araçları doğaları gereği temkinlidir. Bazı özellikleri yalnızca kodu analiz ederek belirlemek imkânsızdır; en bilinen örnek Duruş Problemi’dir.

Bu yüzden derleyici kurallara uyulduğundan emin değilse, doğru bir programı bile reddedebilir. Bu rahatsız edicidir ama felaket değildir. Buna karşılık yanlış programı kabul etseydi, Rust’ın verdiği güvencelere güvenemezdik. RefCell<T>, kurallara uyduğunuzdan emin olduğunuz ama derleyicinin bunu kanıtlayamadığı durumlarda işe yarar.

RefCell<T> de Rc<T> gibi yalnızca tek iş parçacıklı kullanım içindir. Çok iş parçacıklı bağlamda kullanırsanız derleme hatası alırsınız. 16. bölümde, aynı işlevselliğin çok iş parçacıklı sürümünü göreceğiz.

Box<T>, Rc<T> ve RefCell<T> arasında seçim yaparken akılda tutulacak kısa özet şöyledir:

  • Rc<T> aynı verinin birden çok sahibi olmasına izin verir; Box<T> ve RefCell<T> tek sahiplidir.
  • Box<T>, derleme zamanında denetlenen değiştirilemez ya da değiştirilebilir ödünçler sunar; Rc<T> yalnızca derleme zamanında denetlenen değiştirilemez ödünçler sunar; RefCell<T> ise çalışma zamanında denetlenen her iki türü de sunar.
  • RefCell<T> çalışma zamanında denetlenen değiştirilebilir ödünçlere izin verdiği için, kendisi değiştirilemez olsa bile içindeki değeri değiştirebilirsiniz.

Değiştirilemez bir değerin içindeki veriyi değiştirmek, işte bu içsel değiştirilebilirlik desenidir.

İçsel Değiştirilebilirlik Kullanmak

Ödünç alma kurallarının sonucu olarak, elinizde değiştirilemez bir değer varken onu değiştirilebilir olarak ödünç alamazsınız. Örneğin şu kod derlenmez:

fn main() {
    let sayi = 5;
    let degistirilebilir_referans = &mut sayi;
}

Derlerseniz şu hatayı alırsınız:

$ cargo run
   Compiling odunc-alma v0.1.0 (file:///projects/odunc-alma)
error[E0596]: cannot borrow `sayi` as mutable, as it is not declared as mutable
 --> src/main.rs:3:13
  |
3 |     let degistirilebilir_referans = &mut sayi;
  |             ^^^^^^ cannot borrow as mutable
  |
help: consider changing this to be mutable
  |
2 |     let mut sayi = 5;
  |         +++

For more information about this error, try `rustc --explain E0596`.
error: could not compile `odunc-alma` (bin "odunc-alma") due to 1 previous error

Bununla birlikte, bazı durumlarda bir değerin kendi metotları içinde kendini değiştirmesi ama dış dünyaya değiştirilemez görünmesi faydalıdır. RefCell<T> bunu yapmanın yollarından biridir. Tam anlamıyla kurallardan kaçmaz; yalnızca kontrolü derleme zamanından çalışma zamanına taşır. Kuralı ihlal ederseniz derleme hatası değil panic! alırsınız.

Şimdi RefCell<T>yi gerçekten işimize yarayan bir örnek üzerinde görelim.

Sahte Nesnelerle Test Yazmak

Test sırasında programcı bazen bir türün yerine başka bir tür kullanır; amaç belirli davranışı gözlemek ve doğru uygulanıp uygulanmadığını denetlemektir. Bu geçici türe test double denir. Bunun özel bir biçimi olan mock object, test boyunca neler olduğunu kaydeder; böylece doğru eylemlerin gerçekleşip gerçekleşmediğini doğrulayabilirsiniz.

Rust’ta bazı dillerdeki anlamıyla nesne yoktur ve standart kütüphanede hazır mock altyapısı gelmez. Ama aynı işi görecek struct’ları rahatlıkla tanımlayabilirsiniz.

Şu senaryoyu test edelim: bir değerin üst sınıra ne kadar yaklaştığını izleyen ve mevcut değerin sınıra yaklaşmasına göre mesaj gönderen bir kütüphane yazacağız. Örneğin bir kullanıcının yapabileceği API çağrısı kotasını izlemek için kullanılabilir.

Bu kütüphane yalnızca sınıra yakınlığı ve hangi eşiklerde hangi mesajın gönderileceğini bilir. Mesajların nasıl gönderileceğini ise kütüphaneyi kullanan uygulama sağlayacaktır. Bunun için Iletici adlı bir trait tanımlıyoruz. Liste 15-20 kütüphane kodunu gösterir.

Filename: src/lib.rs
pub trait Iletici {
    fn gonder(&self, ileti: &str);
}

pub struct SinirIzleyici<'a, T: Iletici> {
    iletici: &'a T,
    deger: usize,
    en_buyuk: usize,
}

impl<'a, T> SinirIzleyici<'a, T>
where
    T: Iletici,
{
    pub fn yeni(iletici: &'a T, en_buyuk: usize) -> SinirIzleyici<'a, T> {
        SinirIzleyici {
            iletici,
            deger: 0,
            en_buyuk,
        }
    }

    pub fn deger_ata(&mut self, deger: usize) {
        self.deger = deger;

        let en_buyugun_yuzdesi = self.deger as f64 / self.en_buyuk as f64;

        if en_buyugun_yuzdesi >= 1.0 {
            self.iletici.gonder("Hata: Kotanizi astiniz!");
        } else if en_buyugun_yuzdesi >= 0.9 {
            self.iletici
                .gonder("Acil uyari: Kotanizin %90'ini gectiniz!");
        } else if en_buyugun_yuzdesi >= 0.75 {
            self.iletici.gonder("Uyari: Kotanizin %75'ini gectiniz!");
        }
    }
}
Listing 15-20: Bir degerin ust sinira ne kadar yaklastigini izleyen ve belirli seviyelerde uyaran kutuphane

Buradaki önemli noktalardan biri, Iletici trait’inin selfi değiştirilemez referans alan gonder metodunu tanımlamasıdır. Sahte nesnemiz, gerçek nesneyle aynı şekilde kullanılabilmek için bu arayüzü uygulamalıdır. İkinci önemli nokta ise SinirIzleyici üzerindeki deger_ata davranışını test etmek istememizdir. deger parametresine verdiğimiz şeyi değiştirebiliriz ama deger_ata bize doğrulama yapacağımız bir sonuç döndürmez. Biz de “belirli bir en_buyuk değeriyle oluşturulmuş SinirIzleyici, farklı sayılar verildiğinde doğru iletileri gönderiyor mu?” sorusunu sınamak isteriz.

Gerçekten e-posta ya da mesaj göndermek yerine, yalnızca gönderilmesi istenen mesajları kaydeden bir sahte nesneye ihtiyacımız var. Sahte nesnenin örneğini oluşturup SinirIzleyiciye verecek, sonra deger_ata çağıracak ve sonrasında beklediğimiz mesajların kaydedilip kaydedilmediğine bakacağız. Liste 15-21 bu yönde bir girişimi gösteriyor; ama ödünç alma denetleyicisi buna izin vermiyor.

Filename: src/lib.rs
pub trait Iletici {
    fn gonder(&self, ileti: &str);
}

pub struct SinirIzleyici<'a, T: Iletici> {
    iletici: &'a T,
    deger: usize,
    en_buyuk: usize,
}

impl<'a, T> SinirIzleyici<'a, T>
where
    T: Iletici,
{
    pub fn yeni(iletici: &'a T, en_buyuk: usize) -> SinirIzleyici<'a, T> {
        SinirIzleyici {
            iletici,
            deger: 0,
            en_buyuk,
        }
    }

    pub fn deger_ata(&mut self, deger: usize) {
        self.deger = deger;

        let en_buyugun_yuzdesi = self.deger as f64 / self.en_buyuk as f64;

        if en_buyugun_yuzdesi >= 1.0 {
            self.iletici.gonder("Hata: Kotanizi astiniz!");
        } else if en_buyugun_yuzdesi >= 0.9 {
            self.iletici
                .gonder("Acil uyari: Kotanizin %90'ini gectiniz!");
        } else if en_buyugun_yuzdesi >= 0.75 {
            self.iletici.gonder("Uyari: Kotanizin %75'ini gectiniz!");
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    struct SahteIletici {
        gonderilen_iletiler: Vec<String>,
    }

    impl SahteIletici {
        fn yeni() -> SahteIletici {
            SahteIletici {
                gonderilen_iletiler: vec![],
            }
        }
    }

    impl Iletici for SahteIletici {
        fn gonder(&self, ileti: &str) {
            self.gonderilen_iletiler.push(String::from(ileti));
        }
    }

    #[test]
    fn yuzde_75_ustu_uyari_iletisi_gonderir() {
        let sahte_iletici = SahteIletici::yeni();
        let mut sinir_izleyici = SinirIzleyici::yeni(&sahte_iletici, 100);

        sinir_izleyici.deger_ata(80);

        assert_eq!(sahte_iletici.gonderilen_iletiler.len(), 1);
    }
}
Listing 15-21: Odunc alma denetleyicisinin izin vermedigi SahteIletici gerceklemesi denemesi

Bu test kodu, gönderilen iletileri tutmak için Vec<String> kullanan SahteIletici yapısını tanımlar. Boş ileti listesiyle başlayan örnekler oluşturmayı kolaylaştırmak için yeni ilişkili fonksiyonunu da ekleriz. Ardından Iletici trait’ini uygularız.

Testte, SinirIzleyiciye deger olarak 80 verdiğimizde, yani 100lük sınırın yüzde 75’ini geçtiğimizde ne olduğunu sınarız. Önce yeni bir SahteIletici, sonra ona referans verilen bir SinirIzleyici oluştururuz. deger_ata(80) çağırdıktan sonra, sahte ileticinin bir mesaj kaydetmiş olmasını bekleriz.

Ama burada bir sorun var:

$ cargo test
   Compiling sinir-izleyici v0.1.0 (file:///projects/sinir-izleyici)
error[E0596]: cannot borrow `self.gonderilen_iletiler` as mutable, as it is behind a `&` reference
  --> src/lib.rs:58:13
   |
58 |             self.gonderilen_iletiler.push(String::from(ileti));
   |             ^^^^^^^^^^^^^^^^^^^^^^^^ `self` is a `&` reference, so the data it refers to cannot be borrowed as mutable
   |
help: consider changing this to be a mutable reference in the `impl` method and the `trait` definition
   |
 2 ~     fn gonder(&mut self, ileti: &str);
 3 | }
...
56 |     impl Iletici for SahteIletici {
57 ~         fn gonder(&mut self, ileti: &str) {
   |

For more information about this error, try `rustc --explain E0596`.
error: could not compile `sinir-izleyici` (lib test) due to 1 previous error

gonder, selfi değiştirilemez referans aldığı için SahteIletici içindeki ileti listesini değiştiremiyoruz. Hata mesajının önerdiği gibi trait’i ve uygulamayı &mut self yapamayız; çünkü sırf test kolaylığı için Iletici trait’ini değiştirmek istemiyoruz.

İşte burada içsel değiştirilebilirlik devreye girer. gonderilen_iletiler alanını RefCell<T> içine alırız; böylece gonder metodu self değiştirilemez referans alsa bile, içerideki veriyi değiştirebilir. Liste 15-22 bunu gösterir.

Filename: src/lib.rs
pub trait Iletici {
    fn gonder(&self, ileti: &str);
}

pub struct SinirIzleyici<'a, T: Iletici> {
    iletici: &'a T,
    deger: usize,
    en_buyuk: usize,
}

impl<'a, T> SinirIzleyici<'a, T>
where
    T: Iletici,
{
    pub fn yeni(iletici: &'a T, en_buyuk: usize) -> SinirIzleyici<'a, T> {
        SinirIzleyici {
            iletici,
            deger: 0,
            en_buyuk,
        }
    }

    pub fn deger_ata(&mut self, deger: usize) {
        self.deger = deger;

        let en_buyugun_yuzdesi = self.deger as f64 / self.en_buyuk as f64;

        if en_buyugun_yuzdesi >= 1.0 {
            self.iletici.gonder("Hata: Kotanizi astiniz!");
        } else if en_buyugun_yuzdesi >= 0.9 {
            self.iletici
                .gonder("Acil uyari: Kotanizin %90'ini gectiniz!");
        } else if en_buyugun_yuzdesi >= 0.75 {
            self.iletici.gonder("Uyari: Kotanizin %75'ini gectiniz!");
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::cell::RefCell;

    struct SahteIletici {
        gonderilen_iletiler: RefCell<Vec<String>>,
    }

    impl SahteIletici {
        fn yeni() -> SahteIletici {
            SahteIletici {
                gonderilen_iletiler: RefCell::new(vec![]),
            }
        }
    }

    impl Iletici for SahteIletici {
        fn gonder(&self, ileti: &str) {
            self.gonderilen_iletiler
                .borrow_mut()
                .push(String::from(ileti));
        }
    }

    #[test]
    fn yuzde_75_ustu_uyari_iletisi_gonderir() {
        // --snip--
        let sahte_iletici = SahteIletici::yeni();
        let mut sinir_izleyici = SinirIzleyici::yeni(&sahte_iletici, 100);

        sinir_izleyici.deger_ata(80);

        assert_eq!(sahte_iletici.gonderilen_iletiler.borrow().len(), 1);
    }
}
Listing 15-22: Dis deger degistirilemez sayilirken ic degeri degistirmek icin RefCell<T> kullanmak

gonderilen_iletiler alanı artık Vec<String> değil RefCell<Vec<String>>dir. yeni içinde boş vektörün etrafına yeni bir RefCell örneği sararız.

gonder uygulamasında ilk parametre hâlâ selfin değiştirilemez ödüncüdür; trait tanımıyla uyumludur. self.gonderilen_iletiler üzerinde borrow_mut çağırarak içteki vektöre değiştirilebilir erişim alır, ardından push kullanarak iletiyi kaydederiz.

Doğrulamada da vektörün boyuna bakmak için borrow çağırıp değiştirilemez referans alırız.

Ödünçleri Çalışma Zamanında İzlemek

Normal referanslarda & ve &mut kullanırız. RefCell<T> ileyse borrow ve borrow_mut kullanırız. borrow, Ref<T>; borrow_mut ise RefMut<T> döndürür. Her iki tür de Deref uyguladığı için normal referanslar gibi davranabilir.

RefCell<T>, o anda etkin olan Ref<T> ve RefMut<T> akıllı işaretçilerinin sayısını izler. borrow her çağrıldığında değiştirilemez ödünç sayısını artırır. Ref<T> kapsam dışına çıkınca sayı bir azalır. Tıpkı derleme zamanı kuralları gibi, RefCell<T> de aynı anda çok sayıda değiştirilemez ödünç ya da yalnızca tek değiştirilebilir ödünç olmasına izin verir.

Kural ihlali yaparsak, referanslarda olduğu gibi derleme hatası değil çalışma zamanında panic! alırız. Liste 15-23, Liste 15-22’deki gonder uygulamasının bilerek bozulmuş sürümüdür: aynı kapsam içinde iki değiştirilebilir ödünç oluşturmaya çalışıyoruz.

Filename: src/lib.rs
pub trait Iletici {
    fn gonder(&self, ileti: &str);
}

pub struct SinirIzleyici<'a, T: Iletici> {
    iletici: &'a T,
    deger: usize,
    en_buyuk: usize,
}

impl<'a, T> SinirIzleyici<'a, T>
where
    T: Iletici,
{
    pub fn yeni(iletici: &'a T, en_buyuk: usize) -> SinirIzleyici<'a, T> {
        SinirIzleyici {
            iletici,
            deger: 0,
            en_buyuk,
        }
    }

    pub fn deger_ata(&mut self, deger: usize) {
        self.deger = deger;

        let en_buyugun_yuzdesi = self.deger as f64 / self.en_buyuk as f64;

        if en_buyugun_yuzdesi >= 1.0 {
            self.iletici.gonder("Hata: Kotanizi astiniz!");
        } else if en_buyugun_yuzdesi >= 0.9 {
            self.iletici
                .gonder("Acil uyari: Kotanizin %90'ini gectiniz!");
        } else if en_buyugun_yuzdesi >= 0.75 {
            self.iletici.gonder("Uyari: Kotanizin %75'ini gectiniz!");
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::cell::RefCell;

    struct SahteIletici {
        gonderilen_iletiler: RefCell<Vec<String>>,
    }

    impl SahteIletici {
        fn yeni() -> SahteIletici {
            SahteIletici {
                gonderilen_iletiler: RefCell::new(vec![]),
            }
        }
    }

    impl Iletici for SahteIletici {
        fn gonder(&self, ileti: &str) {
            let mut ilk_odunc = self.gonderilen_iletiler.borrow_mut();
            let mut ikinci_odunc = self.gonderilen_iletiler.borrow_mut();

            ilk_odunc.push(String::from(ileti));
            ikinci_odunc.push(String::from(ileti));
        }
    }

    #[test]
    fn yuzde_75_ustu_uyari_iletisi_gonderir() {
        let sahte_iletici = SahteIletici::yeni();
        let mut sinir_izleyici = SinirIzleyici::yeni(&sahte_iletici, 100);

        sinir_izleyici.deger_ata(80);

        assert_eq!(sahte_iletici.gonderilen_iletiler.borrow().len(), 1);
    }
}
Listing 15-23: Ayni kapsamda iki degistirilebilir referans olusturup RefCell<T>nin panic vermesini gormek

Önce borrow_mutten dönen RefMut<T> için ilk_odunc değişkenini, ardından aynı kapsamda ikinci bir RefMut<T> için ikinci_oduncu oluşturuyoruz. Bu, aynı kapsamda iki değiştirilebilir referans demektir ve yasaktır. Kod derlenir ama test başarısız olur:

$ cargo test
   Compiling sinir-izleyici v0.1.0 (file:///projects/sinir-izleyici)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 0.91s
     Running unittests src/lib.rs (target/debug/deps/sinir_izleyici-e599811fa246dbde)

running 1 test
test tests::yuzde_75_ustu_uyari_iletisi_gonderir ... FAILED

failures:

---- tests::yuzde_75_ustu_uyari_iletisi_gonderir stdout ----

thread 'tests::it_sends_an_over_75_percent_warning_message' panicked at src/lib.rs:60:53:
RefCell already borrowed
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    tests::yuzde_75_ustu_uyari_iletisi_gonderir

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

error: test failed, to rerun pass `--lib`

Kodun already borrowed: BorrowMutError mesajıyla paniklediğine dikkat edin. RefCell<T>, kuralları çalışma zamanında işte böyle uygular.

Bu yaklaşımın bedeli vardır: hataları geliştirme sürecinde daha geç fark edebilirsiniz ve çalışma zamanında küçük de olsa ek maliyet oluşur. Buna rağmen, yalnızca değiştirilemez değerlerin izin verildiği bağlamda kendini değiştirebilen sahte nesneler yazmak gibi durumlarda RefCell<T> çok işe yarar.

Değiştirilebilir Verinin Birden Fazla Sahibi Olmasına İzin Vermek

RefCell<T> çok sık Rc<T> ile birlikte kullanılır. Rc<T>, verinin birden çok sahibi olmasına izin verir ama yalnızca değiştirilemez erişim sunar. Eğer Rc<T> içinde RefCell<T> taşırsanız, hem birden çok sahipliğe hem de değiştirilebilirliğe sahip olursunuz.

Liste 15-18’de Rc<T> kullanarak bir listenin birden çok yerde paylaşılabildiğini görmüştük. Ama Rc<T> yalnızca değiştirilemez değerleri tuttuğu için, liste oluştuktan sonra içindeki değerleri değiştiremiyorduk. Şimdi RefCell<T> ekleyerek bunu mümkün kılacağız. Liste 15-24 bunu gösterir.

Filename: src/main.rs
#[derive(Debug)]
enum List {
    Dugum(Rc<RefCell<i32>>, Rc<List>),
    Bos,
}

use crate::List::{Bos, Dugum};
use std::cell::RefCell;
use std::rc::Rc;

fn main() {
    let deger = Rc::new(RefCell::new(5));

    let a = Rc::new(Dugum(Rc::clone(&deger), Rc::new(Bos)));

    let b = Dugum(Rc::new(RefCell::new(3)), Rc::clone(&a));
    let c = Dugum(Rc::new(RefCell::new(4)), Rc::clone(&a));

    *deger.borrow_mut() += 10;

    println!("a sonrası = {a:?}");
    println!("b sonrası = {b:?}");
    println!("c sonrası = {c:?}");
}
Listing 15-24: Degistirilebilir Liste olusturmak icin Rc<RefCell<i32>> kullanmak

Önce Rc<RefCell<i32>> türünden deger oluşturuyoruz ki sonra doğrudan erişebilelim. Ardından a listesini, degeri taşıyan Dugum varyantıyla kuruyoruz. Burada degeri klonlamamız gerekir; böylece içteki 5in sahipliği hem degerde hem ada olur.

a listesini Rc<T> içine sarıyoruz ki b ve c oluşturulurken ikisi de ayı işaret edebilsin.

Listeler kurulduktan sonra degere 10 eklemek istiyoruz. Bunun için borrow_mut çağırıyoruz. Rust’ın otomatik başvuru çözmesi, Rc<T>yi içteki RefCell<T>ye indirger. borrow_mut, RefMut<T> döndürür; biz de bunu çözerek iç değeri değiştiririz.

a, b ve cyi yazdırdığımızda hepsinin artık 15 içerdiğini görürüz:

$ cargo run
   Compiling kons-liste v0.1.0 (file:///projects/kons-liste)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.63s
     Running `target/debug/kons-liste`
a sonrasi = Dugum(RefCell { value: 15 }, Bos)
b sonrasi = Dugum(RefCell { value: 3 }, Dugum(RefCell { value: 15 }, Bos))
c sonrasi = Dugum(RefCell { value: 4 }, Dugum(RefCell { value: 15 }, Bos))

Bu teknik oldukça kullanışlıdır. Dışarıdan bakınca değiştirilemez bir Liste gibi görünür; ama RefCell<T>nin sunduğu API ile gerektiğinde iç veriyi değiştirebiliriz. Çalışma zamanındaki ödünç denetimi veri yarışlarını önler; bazı veri yapılarında biraz performans kaybına karşılık bu esneklik gayet değerlidir. Elbette RefCell<T> çok iş parçacıklı kodda çalışmaz; onun güvenli karşılığı olan Mutex<T>yi 16. bölümde göreceğiz.