İçeriğe geç
academia.sh

Ders 03 / 14

Ödünç Alma

Bir değer sahipliği hiç vermeden bir referans üzerinden okunabilir ve yazılabilir: ödünç sürerken defterde hiçbir kayıt oluşmaz, ödünç kapsamı kapandığında değer hâlâ sahibinde erişilebilir durumdadır. Paylaşımlı ve değiştirilebilir referans birbirini dışlar; bir referansın kapsamı, kaynağın kapsamının her zaman içinde kalmak zorundadır.

Yayın Güncelleme

Bu derste

  • Ödünç alınan bir değerin ödünç sürerken hiçbir yere bitmediğini ve ödünç kapsamı kapandıktan sonra hâlâ sahibinde erişilebilir olduğunu koşturarak gösterir
  • Paylaşımlı ve değiştirilebilir referans arasındaki farkı örnekleyerek ayırt eder
  • Bir referansın kapsamının kaynağın kapsamı içinde kalmak zorunda olduğunu iç içe kapsamlarla kanıtlar
İçindekiler

Önceki ders sahiplik devrettiğinde bitiş noktasının nasıl taşındığını ölçtü — taşınan değerin eski adı geçerliliğini kaybediyordu. Bu ders sahiplik hiç devredilmeden bir değere nasıl erişildiğini ölçüyor: ödünç alma. Soru şu: bir değeri ödünç veren ad değeri kaybediyor mu ve ödünç alınan değer ödünç sürerken bir yere bitiyor mu? Eşzamanlılık kursunun (M09/K03) “bir goroutine nasıl duracak” sorusuyla karışmaz: orada koşan bir birim durur, burada bir değer biter.

Önceki dersin son bölümü, bir değerin bir işleve tek yönlü ve geri dönüşsüz taşınabileceğini gösterdi — ya da sahip zincir boyunca değişse de değer sonunda mutlaka bir yerde bitti. Bu ders aynı işlevlere geçen değerlerde farklı bir yol açıyor: bir değeri hiç taşımadan, yalnız ona geçici bir erişim vererek de aynı işi yapabiliyoruz. İki yol da geçerli, ama sonuçları kaynakta ayrı ayrı okunabiliyor.

Programlama Temelleri kursu (M01/K02) bir değerin bir işleve referans olarak nasıl geçtiğini kavram olarak kurmuştu. Bu derste ölçülen, Rust’ın bu geçişi iki ayrı, birbirini dışlayan biçime ayırması — paylaşımlı ve değiştirilebilir referans — ve bu ayrımı derleme zamanında sınaması.

Ödünç Almak Sahiplik Vermek Değildir

Bir referans (&T ya da &mut T), işaret ettiği değerin sahibi değildir; yalnız o değere erişim sağlayan ayrı, küçük bir değerdir. Referansın kendisinin de bir kapsamı var ve o kapsam kapandığında referans biter, ama referansın bitmesi işaret ettiği değeri etkilemez — çünkü referans zaten o değerin sahibi olmamıştı, kapsamı kapanırken Drop’u tetikleyecek bir şeyi yoktur.

Rust iki tür referansı ayırıyor. Paylaşımlı referans (&T) aynı değere aynı anda birden çok kez alınabilir ve yalnız okumaya izin verir. Değiştirilebilir referans (&mut T) aynı anda yalnız bir tane olabilir ve değeri okumanın yanında değiştirmeye de izin verir. İkisi aynı anda, aynı değer için bir arada bulunamaz.

Bu iki tür bir seçenek değil, bir zorunluluk. Bir değere erişmek isteyen her kod parçası — bir işlev parametresi, bir yerel bağlama, bir yapı yöntemi — hangi türü istediğini önceden söylemek zorunda; söz dizimindeki tek fark bir mut anahtar sözcüğü olsa da, ürettiği yetki tamamen farklı. Bu ayrım kaynakta görünür olduğu için, bir okuyucu bir referansın değeri değiştirip değiştiremeyeceğini, o referansın alındığı satıra bakarak söyleyebiliyor — değerin kendi tanımına ya da nasıl kullanıldığına bakmadan.

SK7. Bir referans kendi başına bir değerdir ve kendi kapsamı vardır, ama Iz gibi Drop uygulayan bir tipin referansı Drop’u uygulamaz. &Iz ya da &mut Iz tipinde bir değer kapsamının sonunda sessizce yok olur; deftere hiçbir şey yazmaz, çünkü yazma işini yapan kod yalnız Izin kendi drop yönteminde ve o yöntem yalnız sahip olunan bir Iz bittiğinde çağrılır.

Bu ayrım önceki dersle doğrudan karşıtlık kuruyor. Sahiplik ve Taşıma dersinde bir işleve geçirilen Iz değeri, işlevin parametresine taşınıyordu — işlev bitince değer de bitiyordu ve aynı değeri ikinci bir çağrıya geçirmek mümkün değildi, çünkü ilk çağrıdan sonra eski ad geçersiz kalıyordu. Bir işlev değeri ödünç alıyorsa bu kısıt ortadan kalkıyor: işlev değeri kullanıp bırakıyor, sahiplik hiç el değiştirmiyor, çağıran taraf aynı değeri istediği kadar tekrar kullanabiliyor.

Bu dersin bütün ölçümleri önceki dersin çekirdeğinden aynı kesiti kullanıyor — Iz, Drop uygulaması ve gözlem defteri; yardımcı kısım ayrı bir dosyaya konuyor ki sonraki örnekler onu tekrar tekrar mod ile çekebilsin.

// iz.rs — bir degerin bitisini kaydeden olcum yardimcisi
use std::cell::RefCell;
use std::rc::Rc;

pub type Defter = Rc<RefCell<Vec<String>>>;

pub struct Iz {
    pub ad: String,
    defter: Defter,
}

impl Iz {
    pub fn yeni(ad: &str, defter: &Defter) -> Iz {
        Iz { ad: ad.to_string(), defter: Rc::clone(defter) }
    }
}

impl Drop for Iz {
    fn drop(&mut self) {
        self.defter.borrow_mut().push(self.ad.clone());
    }
}

pub fn yeni_defter() -> Defter {
    Rc::new(RefCell::new(Vec::new()))
}

pub fn okunan(defter: &Defter) -> Vec<String> {
    defter.borrow().clone()
}
// odunc_islev.rs — bir islevin degeri odunc alip iade etmesi, tasima ile karsitligi
mod iz;
use iz::{Iz, yeni_defter, okunan};

fn oku(referans: &Iz) -> usize {
    referans.ad.len()
}

fn main() {
    let defter = yeni_defter();
    let deger = Iz::yeni("paylasilan", &defter);

    let uzunluk1 = oku(&deger);
    let uzunluk2 = oku(&deger);
    println!("ilk okuma: {}, ikinci okuma: {}", uzunluk1, uzunluk2);
    println!("iki cagridan sonra defter: [{}]", okunan(&defter).join(", "));
}
ilk okuma: 10, ikinci okuma: 10
iki cagridan sonra defter: []

oku işlevi degeri iki kez, art arda ödünç alıyor. Sahiplik ve Taşıma dersindeki yut işlevi aynı değeri ikinci kez alacak bir çağrıyla derlenemezdi, çünkü ilk çağrı zaten sahipliği tüketmişti. Burada iki çağrı da geçerli, çünkü oku hiçbir zaman sahiplik istemiyor — parametre tipi &Iz, Iz değil. İki çağrıdan sonra bile defter boş: deger mainin sonuna kadar sahibinde yaşamaya devam ediyor.

oku(&deger) çağrısındaki & işareti burada zorunlu — işlev bir referans istiyor, kaynak bunu açıkça vermek zorunda. Kodun başka yerlerinde (a.ad, deger.ad) aynı zorunluluk görünmüyor: bir alana . ile erişmek ya da bir yöntem çağırmak, gerektiğinde derleyici tarafından örtük olarak ödünç alınıyor. referans.ad.len() satırı da böyle — referans zaten bir &Iz, ama .ad erişimi ile .len() çağrısı ayrı bir & yazmayı gerektirmiyor. Örtük ödünç alma yalnız kaynağın okunabilirliğini kolaylaştırıyor; sınırları (paylaşımlı mı değiştirilebilir mi, ne kadar sürüyor) aynı kurallara tabi.

Ölçüm: Ödünç Sürerken Defter Boş

Aşağıdaki ölçüm üç kapsam seviyesini iç içe koyuyor: en dışta main, onun içinde degerin kendi sahiplik kapsamı, onun içinde de sırayla paylaşımlı ve değiştirilebilir ödünç kapsamları. Yardımcı dosya (iz.rs) zaten çalışma dizininde; bu blok onu tekrar mod iz; ile çekiyor.

// odunc.rs — odunc alinan deger, odunc surerken hicbir yere bitmiyor
mod iz;
use iz::{Iz, yeni_defter, okunan};

fn main() {
    let defter = yeni_defter();

    {
        let mut deger = Iz::yeni("odunc-1", &defter);

        {
            let a = &deger;
            let b = &deger;
            println!("paylasimli okuma: {} ve {}", a.ad, b.ad);
            println!("odunc surerken defter: [{}]", okunan(&defter).join(", "));
        }
        println!("paylasimli odunc bittikten sonra defter: [{}]", okunan(&defter).join(", "));

        {
            let m = &mut deger;
            m.ad.push_str("-not");
            println!("degistirilebilir odunc icinde ad: {}", m.ad);
        }
        println!("degistirilebilir odunc bittikten sonra defter: [{}]", okunan(&defter).join(", "));
        println!("deger hala erisilebilir, kendi adi: {}", deger.ad);
    }
    println!("dis kapsam bittikten sonra defter: [{}]", okunan(&defter).join(", "));
}
paylasimli okuma: odunc-1 ve odunc-1
odunc surerken defter: []
paylasimli odunc bittikten sonra defter: []
degistirilebilir odunc icinde ad: odunc-1-not
degistirilebilir odunc bittikten sonra defter: []
deger hala erisilebilir, kendi adi: odunc-1-not
dis kapsam bittikten sonra defter: [odunc-1-not]

Beş ayrı ölçüm noktasının dördü defterin boş olduğunu gösteriyor — paylaşımlı ödünç sürerken, paylaşımlı ödünç bittikten sonra, değiştirilebilir ödünç sürerken (satır kendi içinde basıldığı için ayrıca görünmüyor ama bir sonraki satır bunu doğruluyor) ve değiştirilebilir ödünç bittikten sonra. deger yalnız en dıştaki kapsam (main’in kendi gövdesi) kapandığında bitiyor ve defter ancak o zaman odunc-1-not yazısını alıyor. a ve bnin ikisinin de aynı anda var olabilmesi — ikisi de degeri yalnız okuyor, birbirine karışmıyor — paylaşımlı referansın çoğulluğunu gösteriyor.

Çıktıdaki son satır (odunc-1-not) bunun da ötesinde bir şey kanıtlıyor: degerin kendisi hiçbir zaman yeniden oluşturulmadı. m üzerinden yapılan değişiklik, degerin kendi ad alanına yazıldı; deger bittiğinde deftere düşen isim, oluşturulduğu andaki odunc-1 değil, ödünç sırasında değiştirilmiş hâli. Bu, ödünç almanın salt-okunur bir görüntü değil, gerçek bir erişim yolu olduğunu gösteriyor — paylaşımlı referans yalnız okurken, değiştirilebilir referans kaynağın kendisini kalıcı olarak değiştirebiliyor.

Paylaşımlı ve Değiştirilebilir Referans

m.ad.push_str("-not") satırı, değiştirilebilir bir referans üzerinden degerin kendi alanını gerçekten değiştiriyor — bu satırdan sonra hem m.ad hem de kapsam kapandıktan sonra okunan deger.ad yeni değeri (odunc-1-not) taşıyor. Paylaşımlı referanslarla (a, b) bu mümkün değildi: a.ad’i değiştirmeye çalışan bir satır derleyici tarafından reddedilirdi, çünkü &T yalnız okuma izni veriyor.

SK8. Paylaşımlı referansların aynı anda birden çok olabilmesi, hiçbirinin değeri değiştirememesinden kaynaklanıyor — okuma birbirine karışmaz. Değiştirilebilir bir referansın aynı anda yalnız bir tane olabilmesi ise tam tersi bir nedenden: bir değişiklik başka bir okumanın ya da başka bir değişikliğin ortasında beklenmeyen bir sonuca yol açabilir. Kural referansların sayısına değil, aralarında bir değişikliğin var olup olmadığına bakıyor.

Bu ayrımın somut karşılığı, ödünç almanın kendisinin de iki ayrı yetki taşıması. oku işlevi yalnız &Iz istediği için değeri değiştiremez — parametre tipinin kendisi bunu engelliyor, işlevin gövdesine bakmaya bile gerek yok. Bir işlev değeri değiştirmek istiyorsa parametresini &mut Iz olarak yazmak zorunda ve bu durumda o işlev aynı değere aynı anda ödünç alınmış başka hiçbir referansla bir arada çağrılamıyor. Bu, bir işlev imzasının yalnız hangi tipi aldığını değil, o tipe ne yapabileceğini de baştan belirlediği anlamına geliyor — Rust’ın Tasarım Hedefleri dersinde (M10/K01) sınıfın imzadan okunması gibi, burada da yetki imzadan okunuyor.

Redd: Aynı Anda Bir Yazan ya da Çok Okuyan

Yukarıdaki kod paylaşımlı ödünç ile değiştirilebilir ödüncü hep ayrı kapsamlarda, art arda kullandı — hiçbir zaman ikisi birden aynı anda canlı olmadı. Bu tesadüf değil, zorunlu. a ve b hâlâ kapsamdayken aynı satırlara let m = &mut deger; eklenseydi, derleyici reddederdi: aynı değere aynı anda hem paylaşımlı hem değiştirilebilir bir referans birlikte var olamaz. Aynı kural iki değiştirilebilir referans için de geçerli — let m1 = &mut deger; ile let m2 = &mut deger; aynı kapsamda birlikte yaşayamaz, çünkü ikisi de aynı alana yazma izni taşıyor ve hangi yazmanın önce, hangisinin sonra geçerli olacağı kaynaktan okunamaz hâle gelirdi. İki paylaşımlı referans (a ve b) için bu sorun yok, çünkü hiçbiri yazmıyor — okumanın sırası sonucu değiştirmiyor.

Bu ret bir çalışma zamanı denetimi değil; kaynak, iki referansın kapsamları çakıştığı an hiç derlenmiyor. Kabul edilen yazım (yukarıdaki kod) bu çakışmayı zaten baştan dışlıyor: paylaşımlı kapsam kendi süslü parantezleriyle kapanıyor, değiştirilebilir kapsam ondan sonra ayrı süslü parantezlerle açılıyor — iki kapsam hiçbir noktada üst üste binmiyor.

Aynı kısıt işlev çağrılarında da geçerli. oku(&deger) iki kez art arda çağrılabildi, çünkü hiçbir çağrının referansı bir sonraki çağrı başlarken hâlâ canlı değildi — her çağrı kendi ödünç kapsamını açıp kendi içinde kapatıyor. Aynı değeri hem okuya (&Iz) hem de aynı anda değiştiren bir işleve (&mut Iz) birlikte geçirmeye çalışan bir yazım, iki kapsam çakıştığı için derlenmezdi. Kural, tek bir işlevin gövdesiyle sınırlı değil; bir kaynaktaki bütün referans kapsamları aynı denetime tabi.

Sınırlayıcı Ölçüm: Ödünç Ömrü Uzatmaz

Üç kapsam seviyesi (main, degerin kendi kapsamı, ödünç kapsamları) birbirinin içine yerleşiyor ve bu yerleşim tek yönlü: bir ödünç kapsamı her zaman kaynağın kapsamının içinde kalıyor, hiçbir zaman dışına taşmıyor. a ve b, degerin kapsamı kapanmadan önce kendi kapsamlarını kapatıyor; m de öyle. Hiçbir referans, işaret ettiği değerden daha uzun yaşamıyor.

SK9. Bir referansın geçerli olduğu aralık, kaynağının kapsamını hiçbir zaman aşamaz — bu, ödünç almanın ömrü uzatmadığının somut karşılığı. degerin kendisi mainin gövdesi bitene kadar yaşıyor; içindeki hiçbir referans bundan daha uzun bir aralık talep edemiyor, çünkü edebilseydi, o referans kaynağı zaten bitmiş bir değere işaret ederdi.

Bu kısıt burada iç içe kapsamlarla, doğal bir yazımla sağlandı: her ödünç kendi süslü parantezleri içinde açılıp kapandı ve degerin kendi kapsamı bunların hepsini kapsadı. Bir referansı bu iç içe geçmenin dışına, örneğin degerin kapsamından sonra hâlâ geçerli olacak bir yere taşımaya çalışan bir yazım derleyici tarafından reddedilir — bu aralığın kendi adı ve kuralları sıradaki bölümde ele alınacak.

Bu kısıtın neden zorunlu olduğu, tersini düşününce netleşiyor. m adlı değiştirilebilir referans degerin kapsamından daha uzun yaşayabilseydi, deger bitip deftere yazıldıktan sonra bile m üzerinden bir okuma ya da yazma denenebilirdi — ama artık işaret edilecek geçerli bir Iz kalmamış olurdu. Derleyici bu senaryoyu, referansın geçerli olduğu aralığı kaynağın kapsamıyla sınırlayarak baştan engelliyor. oku işlevindeki &Iz parametresi de aynı kısıta tabi: işlev kendi gövdesi içinde bu referansı ne kadar kullanırsa kullansın, referansın aralığı oku çağrısının süresini aşamıyor — çağıran taraftaki deger hâlâ sahibinde, iki çağrının arasında bile referans yeniden alınıyor.

Bu derste ölçülen üç şey birbirine bağlı. Ödünç almak sahiplik vermediği için ödünç alınan değer ödünç sürerken hiçbir yere bitmiyor; referansın kendisi kaynaktan ayrı, kendi kapsamına sahip küçük bir değer olduğu için kaynağın bitişini tetiklemiyor; ve bir referansın kapsamı kaynağın kapsamını hiçbir zaman aşamadığı için “hâlâ yaşayan bir değere işaret etme” garantisi baştan kuruluyor. Paylaşımlı ve değiştirilebilir ayrımı bu üç ölçümün üzerine binen dördüncü bir kısıt: aynı anda birden çok okuma güvenli, ama bir değişiklik başka hiçbir erişimle aynı anda olamıyor. Sahiplik ve Taşıma dersindeki “tek sahip” ilkesi burada “tek yazan, çok okuyan” biçiminde genişliyor — sahiplik hâlâ tek bir adda, ama o adın izniyle birden çok geçici erişim aynı anda var olabiliyor. Bu genişleme, sahibin kendisinin hiçbir zaman devre dışı kalmadığı anlamına da geliyor: deger, kaç referans dağıtırsa dağıtsın, kendi kapsamı içinde tek gerçek sahip olarak kalmaya devam ediyor.

Özet

  • Bir referans, işaret ettiği değerin sahibi değildir; kendi kapsamı kapandığında yalnız kendisi biter, kaynağı etkilemez.
  • Ödünç sürerken defterde hiçbir kayıt oluşmuyor; ödünç alınan değer, ödünç kapsamı kapandıktan sonra da sahibinde erişilebilir kalıyor. Ödünç verilen değerin ömrü kapsam sonunda bitiyor, ödünç bittiğinde değil.
  • Paylaşımlı referans (&T) aynı anda birden çok olabilir ve yalnız okur; değiştirilebilir referans (&mut T) aynı anda yalnız bir tane olabilir ve değiştirebilir.
  • Aynı değere aynı anda hem paylaşımlı hem değiştirilebilir, ya da iki değiştirilebilir referans birlikte var olamaz; bu kural derleme zamanında sınanır.
  • Bir referansın kapsamı, kaynağının kapsamının her zaman içinde kalmak zorundadır — ödünç almak ömrü uzatmaz.
  • Aynı değer, ödünç alan bir işleve arka arkaya birden çok kez geçirilebilir; bu, sahiplik isteyen bir işlevin (yut gibi) yalnız bir kez çağrılabilmesiyle doğrudan karşıt. Bir alana . ile erişmek ya da bir yöntem çağırmak da gerektiğinde örtük olarak ödünç alır.

Sonraki Adım

Bu derste bir değerin tamamına ödünç verilen referanslar ölçüldü — &Iz ya da &mut Iz, hep bütün değere. Sıradaki ders aynı sahiplik-vermeden-erişim ilkesini bir değerin yalnız bir parçasına uyguluyor: bir dizginin ya da bir büyüyen dizinin belirli bir aralığını, kaynağı hiç bitirmeden nasıl görüntüleyebiliriz? Ödünç almanın burada ölçülen bütün kuralları (kaynak bitmiyor, ömür uzamıyor, paylaşımlı/değiştirilebilir ayrımı) aynen geçerli kalacak; değişen yalnız görüntülenen kısmın büyüklüğü.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ ↵ Esc gezin · aç · kapat