Ders 08 / 14
Yaşam Süresi Çıkarım Kuralları
Derleyicinin bir imzada yazılmayan yaşam süresi açıklamasını hangi kurallarla türettiği ölçülüyor. Aynı imza açık ve örtük biçimde yazılıp koşturuluyor ve ikisinin de aynı davranışı verdiği gösteriliyor; çıkarımın kapsamadığı imzada açıklamanın yine zorunlu kaldığı bir redd-kabul çiftiyle sınanıyor.
Yayın Güncelleme
Bu derste
- Yaşam süresi çıkarımının kapsadığı iki imza biçimini tanımlar
- Aynı imzanın açık ve örtük biçimlerini koşturarak aynı davranışı verdiğini gösterir
- Çıkarımın kapsamadığı bir imzada açıklamanın hâlâ zorunlu olduğunu redd-kabul çiftiyle gösterir
İçindekiler
Önceki ders açık yazımın ne zaman gerektiğini ve neyi ifade ettiğini ölçtü. Ama kursun daha
önceki derslerinde geçen işlevlerin çoğu — tek girdi alıp bir referans döndüren, ya da self
üzerinden çalışan yöntemler — hiç açık açıklama taşımadı ve yine de derlendi. Bu ders o
boşluğu dolduruyor: derleyici, yazılmayan açıklamayı hangi kurallarla türetiyor ve bu
kuralların kapsamadığı imzalarda ne oluyor.
Bu soru M09/K01’in “yazılmayanın karşılığı ne” eksenine benziyor ama aynı şey değil: orada ölçülen, yazılmayan bir tür bilgisinin derleyici tarafından nereden tamamlandığıydı; burada ölçülen bir referansın ömrünün nerede bittiği — çıkarım kuralları yalnızca bu bilgiyi kaynağa yazma zorunluluğunu kaldırıyor, bilginin kendisini değiştirmiyor. Eşzamanlılık kursunun (M09/K03) sorduğu “koşan bir birim nasıl duracak” sorusu da burada geçerli değil; bu ders hâlâ bir referansın geçerlilik aralığını ölçüyor, bir çalışma biriminin durmasını değil.
Çıkarımın kendisi yeni bir bilgi kaynağı değil. Önceki derste kurulan ilke geçerliliğini koruyor: derleyici hiçbir zaman tahmin yürütmüyor, yalnızca her zaman aynı sonuca varacağı durumlarda yazımı kısaltıyor. Bir imza biçimi çıkarımın kapsamına girdiğinde, o biçimdeki her imza için tek bir doğru okuma var demektir; girdiden çıktıya giden yol birden fazla olasılık taşımıyor. Bu ders o kapsamın nerede başlayıp nerede bittiğini ölçüyor.
Bu, kaynağı okuyan bir kişi açısından da önemli bir sonuç doğuruyor: bir imzada açıklama
görünmüyorsa, bu iki farklı şeyi ifade edebilir. Ya işlevin dönen değerle hiçbir referans
ilişkisi yok, ya da ilişki var ama imza biçimi çıkarımın kapsadığı iki kalıptan birine giriyor.
Bu ikisi arasındaki fark, imzaya bakıp girdi sayısını ve self’in varlığını kontrol ederek
ayırt ediliyor — aşağıdaki iki ölçüm bu kontrolü somutlaştırıyor.
Üç Kural, İki Kolaylık
Bir imzada açıklama yazılmadığında derleyicinin varsayacağı ilişkiye çıkarım (elision)
deniyor. Rust’ın dil belirtimi bu varsayımı üç kuralla tanımlıyor. İlk kural her girdi
referansına kendi ayrı, adsız bir
ömür veriyor — bu her zaman geçerli ve kendi başına bir çıktı üretmiyor. İkinci kural, girdi
tam olarak bir referansa sahipse, o girdinin ömrünü bütün elenmiş çıktı referanslarına
atıyor. Üçüncü kural, girdiler arasında &self ya da &mut self varsa, self’in ömrünü
bütün elenmiş çıktı referanslarına atıyor.
İkinci ve üçüncü kural, pratikte açıklamayı gereksiz kılan kurallar — ilk kural yalnızca girdi tarafını adlandırıyor, çıktıyı hiçbir girdiye bağlamıyor. Örneğin iki referans alıp hiç referans döndürmeyen bir işlevde yalnızca ilk kural işliyor: her girdi kendi ayrı ömrünü alıyor ve mesele orada bitiyor, çünkü elenecek bir çıktı yok. İkinci ve üçüncü kural devreye ancak elenmiş bir çıktı referansı olduğunda giriyor ve bu çıktının hangi girdiye bağlanacağını belirliyor.
İkinci kuralın “tam olarak bir girdi” koşulu, girdi sayısına değil girdideki referans
sayısına bakıyor. Bir işlev bir sayı ve bir referans birlikte alıyorsa, girdi listesi iki
öğe taşısa da ömür taşıyan girdi hâlâ tek — ikinci kural yine uygulanıyor. Aynı biçimde, bir
işlev tek bir referans alıp birden çok referans içeren bir demet döndürürse, elenmiş her
çıktı parçası da aynı tek girdinin ömrünü alıyor; kural “tek bir çıktıya” değil, “elenmiş
bütün çıktılara” uygulanıyor. Aşağıdaki iki ölçüm bu iki kolaylığı ayrı ayrı gösteriyor: önce tek girdili bir
işlev, sonra &self alan bir yöntem.
Ölçüm: Tek Girdili İmza
Bir işlev yalnızca bir referans alıp bir referans döndürüyorsa, ikinci kural devreye giriyor:
elenmiş tek girdinin ömrü, elenmiş çıktıya atanıyor. Aşağıda aynı işlev iki kez yazılıyor —
biri açıklamasız, biri açık 'a ile — ve ikisi de aynı sonucu veriyor.
// cikarim_tek_girdi.rs — tek girdili imza: aciklama yazilmasa da (elision) ayni davranis.
struct Iz {
ad: String,
}
// Ortuk: derleyici tek girdinin omrunu cikan referansa atiyor.
fn ilk_uc_harf(iz: &Iz) -> &str {
&iz.ad[..3]
}
// Acik: ayni iliski elle yazildi.
fn ilk_uc_harf_acik<'a>(iz: &'a Iz) -> &'a str {
&iz.ad[..3]
}
fn main() {
let deger = Iz { ad: "kapsam-1".to_string() };
println!("ortuk -> {}", ilk_uc_harf(°er));
println!("acik -> {}", ilk_uc_harf_acik(°er));
}
ortuk -> kap acik -> kap
ilk_uc_harf ve ilk_uc_harf_acik aynı gövdeyi taşıyor ve aynı çıktıyı veriyor; aralarındaki
tek fark kaynaktaki açıklamanın görünürlüğü. Derleyici, birincisini derlerken ikincisiyle
aynı ilişkiyi kendisi kuruyor — girdi tek olduğu için başka bir ihtimal de yok: dönen
referans ya iz’den geliyor ya da hiç yok.
YS6. Çıkarım kuralları, derleyicinin her zaman aynı sonuca varacağı durumlarda açıklamayı gereksiz kılıyor. Tek girdili bir imzada dönen referansın hangi girdiye bağlı olacağı konusunda gerçek bir belirsizlik yok — girdi zaten bir tane.
Bu, önceki dersteki oncelikli_ad işleviyle karşılaştırıldığında daha net görülüyor:
oncelikli_ad iki girdi aldığı için açıklamasız yazılamazdı, çünkü dönen referansın
oncelikli’den mi digeri’den mi geldiği kaynaktan çıkarılamazdı. ilk_uc_harf tek girdi
aldığı için aynı belirsizlik hiç oluşmuyor — çıkarım kuralı burada gerçek bir seçim yapmıyor,
yalnızca zaten tek olan seçeneği kaynağa yazmaktan kurtarıyor.
Ölçüm: self Alan Yöntem
Üçüncü kural, bir yöntem self’i ve başka referanslar alsa bile, elenmiş çıktıyı hep
self’in ömrüne bağlıyor. Bu, kursun önceki derslerinde görülen birçok yöntemin neden hiç
açıklama taşımadığını açıklıyor.
// cikarim_self.rs — &self alan yontem: cikan referans self'e baglaniyor, aciklama yazilmasa da.
struct Iz {
ad: String,
}
impl Iz {
// Ortuk: self'in omru cikan referansa atanir.
fn adi(&self) -> &str {
&self.ad
}
}
struct IzAcik {
ad: String,
}
impl IzAcik {
// Acik: ayni atama elle yazildi.
fn adi<'a>(&'a self) -> &'a str {
&self.ad
}
}
fn main() {
let deger = Iz { ad: "kapsam-2".to_string() };
let acik = IzAcik { ad: "kapsam-2".to_string() };
println!("ortuk -> {}", deger.adi());
println!("acik -> {}", acik.adi());
}
ortuk -> kapsam-2 acik -> kapsam-2
Iz::adi ile IzAcik::adi aynı davranışı veriyor. Burada dikkat çeken şey, yöntemin self
dışında başka bir referans alsaydı bile sonucun değişmeyeceği — üçüncü kural yalnızca
self’e bakıyor, diğer girdileri hiç hesaba katmıyor. Bu, ikinci kuraldan farklı bir kaynağa
dayanan aynı sonuç: derleyici, self alan bir yöntemde dönen referansın neredeyse her zaman
self’in bir parçası olduğunu varsayıyor ve bu varsayım yeterince güvenilir kabul edildiği
için kurala dönüştürülmüş.
Bu iki kural bir arada, kursun önceki derslerinde görülen yöntemlerin çoğunun neden hiç
açıklama taşımadığını da açıklıyor: bir tipin kendi alanına referans döndüren yöntemleri
&self alıyordu ve üçüncü kural devreye giriyordu; bir değeri okuyup türetilmiş bir referans
döndüren serbest işlevler çoğunlukla tek girdiliydi ve ikinci kural devreye giriyordu. Açık
açıklamanın gerçekten göründüğü yer, önceki derste olduğu gibi, birden fazla girdinin
self olmadan bir arada bulunduğu imzalardı.
YS7. İki farklı kural, iki farklı imza biçiminde aynı sonucu üretiyor: tek girdili
işlevlerde girdinin kendisi, self alan yöntemlerde self’in kendisi, çıktının ömrünü
belirliyor. Bu iki biçimde açıklama hiç yazılmasa da imza kabul ediliyor; ikisinin de ortak
noktası, dönen referansın gidebileceği tek bir yerin olması.
Bir İmzayı Okumanın Sırası
Yukarıdaki iki ölçüm birlikte, bir imzaya bakıp açıklamanın gerekip gerekmediğini belirlemenin
sırasını da veriyor. Önce girdilerdeki referans sayısı sayılıyor: hiç referans yoksa ya da
elenecek bir çıktı referansı yoksa, ilk kural tek başına yetiyor ve mesele kapanıyor. Girdide
tam olarak bir referans varsa, ikinci kural devreye giriyor ve çıktı ona bağlanıyor —
ilk_uc_harf bu yola giriyor. Girdide birden fazla referans varsa, sıradaki soru self’in
olup olmadığı: varsa üçüncü kural devreye giriyor ve çıktı self’e bağlanıyor — Iz::adi bu
yola giriyor. Birden fazla referans var ve self yoksa, üç kuraldan hiçbiri tek bir sonuca
varamıyor ve açık açıklama zorunlu hâle geliyor — daha_uzun_olan bu son duruma düşüyor.
Bu sıralama, derleyicinin kendi iç işleyişinden çok, bir okuyucunun bir imzaya bakıp açıklama bekleyip beklemeyeceğini hızlıca kestirmesi için kullanışlı bir kısayol. Kurs boyunca görülen işlevlerin çoğu ilk iki basamakta çözülüyor; açık açıklamanın göründüğü yerler, tam da bu sıralamanın son basamağına düşen, birden fazla eşit ağırlıklı referans taşıyan imzalar oluyor.
Bu sıralamanın kendisi bir araç ya da bir doğrulayıcı gerektirmiyor — üç madde ezberlenip herhangi bir imzaya elle uygulanabiliyor. Bu da yaşam süresiyle ilgili önceki ölçümlerle aynı örüntüyü tekrarlıyor: kaynak, kendi başına, çalıştırılmadan okunabilir bir bilgi taşıyor. Farkı, burada okunanın bir referansın aralığı değil, o aralığın yazılıp yazılmadığı kararı olması.
Sınırlayıcı Ölçüm: Çıkarım Kolaylıktır, Kural Değildir
Çıkarım kuralları üç maddeyle sınırlı ve bu üç maddenin kapsamadığı imzalar var. En açık
örnek, self içermeyen ve birden fazla referans alan bir işlev: ne ikinci kural (tek girdi
yok), ne üçüncü kural (self yok) devreye giriyor. Böyle bir işlevde dönen referansın hangi
girdiye bağlı olduğu konusunda derleyicinin kendiliğinden varacağı tek bir sonuç yok —
birinci kural her girdiye kendi ayrı ömrünü veriyor ve hiçbirini çıktıya atamıyor.
Önceki derste görülen iki referanslı, self’siz işlev (oncelikli_ad) tam olarak bu
durumdaydı ve açık 'a taşıyordu — o derste açıklama, iki girdiden yalnızca birine bağlanan
dar bir söz kuruyordu. Aynı sınır burada yeni bir örnekle, bu kez her iki girdinin de gerçekten
dönebildiği bir durumla sınanıyor: iki Iz referansı alıp adı daha uzun olanın dilimini
döndüren bir işlev, açıklamasız yazılırsa derleyici hangi girdinin döndürüldüğünü
belirleyemediği için reddediyor. Gövde, çalışma zamanındaki bir karşılaştırmaya göre x’i de
y’yi de döndürebiliyor — bu da girdilerin eşit ağırlıklı olduğunu, birinin diğerine göre
öncelikli olmadığını gösteriyor. Kabul edilen yazım ise açık 'a ile ikisini de aynı ömre
bağlıyor.
// cikarim_disi.rs — cikarimin kapsamadigi imza: aciklama yazilmadan derlenmiyor, yazilinca derleniyor.
struct Iz {
ad: String,
}
fn daha_uzun_olan<'a>(x: &'a Iz, y: &'a Iz) -> &'a str {
if x.ad.len() >= y.ad.len() { &x.ad } else { &y.ad }
}
fn main() {
let a = Iz { ad: "kisa".to_string() };
let b = Iz { ad: "daha-uzun-ad".to_string() };
println!("{}", daha_uzun_olan(&a, &b));
}
daha-uzun-ad
daha_uzun_olan’ın açıklamasız hâli üç kuraldan hiçbirine uymuyor: iki girdi var, self
yok. Derleyici, dönen referansın x’ten mi y’den mi geldiğini belirleyemediği için imzayı
reddediyor; kabul edilen yazım, önceki dersteki digerinin_adi işlevinde görülen çözümün
aynısını uyguluyor — iki girdiyi de aynı 'a’ya bağlamak. Yukarıdaki koşum bu kabul edilen
biçimle çalışıyor ve iki adı karşılaştırıp uzun olanı döndürüyor.
digerinin_adi ile daha_uzun_olan arasındaki fark, kabul edilme gerekçesinde değil, dönüş
kararının ne zaman belli olduğunda: digerinin_adi’de hangi parametrenin döndürüleceği
işlevin gövdesinde sabitti, daha_uzun_olan’da bu karar her çağrıda yeniden, çalışma
zamanındaki bir karşılaştırmayla veriliyor. İki durumda da açıklama aynı işi görüyor —
dönebilecek her kaynağı önceden kapsıyor — çünkü derleme zamanında hangi dalın seçileceği
bilinmiyor; açıklama olası bütün yolları kapsamak zorunda, yalnızca gerçekte izlenen yolu
değil.
YS8. Çıkarım kuralları bir kolaylıktır, dilin her imzayı derleyebileceği anlamına gelmez. Üç kuraldan hiçbiri bir imzaya uymadığında açıklama yine zorunlu; kural yalnızca ne zaman gerekmediğini belirliyor, her zaman gerekmeyeceğini değil.
Bu sınır, önceki derste kurulan ilkeyle aynı köke dayanıyor: derleyici bir açıklamayı yalnızca
kaynaktan tek bir doğru okuma çıkıyorsa atlıyor. daha_uzun_olan gibi iki eşit ağırlıklı
girdisi olan bir işlevde bu tek okuma yok — x ve y aynı tipte, aynı konumda, ikisi de
dönebilir. Üç kural da bu belirsizliği çözecek bir varsayım içermiyor, çünkü herhangi bir
varsayım bazı doğru programları reddedip bazı yanlış olanları kabul edebilirdi. Rust bu
riski almak yerine yazımı reddediyor ve kararı geliştiriciye bırakıyor; bu da açıklamanın
kolaylık değil güvence olduğu yerde neden hâlâ zorunlu kaldığını gösteriyor.
Özet
- Rust’ın çıkarım kuralları, yaşam süresi açıklamasının bazı imza biçimlerinde
yazılmasını gereksiz kılıyor; bunlardan ikisi kursta ölçüldü: tek girdili işlevler ve
selfalan yöntemler. - Tek girdili bir işlevde elenmiş girdinin ömrü elenmiş çıktıya atanıyor;
selfalan bir yöntemde bu atama girdi sayısından bağımsız olarak hepself’e yapılıyor. - Aynı imzanın açık ve örtük biçimleri, ölçülen iki durumda da birebir aynı davranışı verdi; iki biçimde de değerler kapsam sonunda bitti.
- Bu ders M09/K01’in yazılmayan tür bilgisini ölçen ekseninden farklı: burada ölçülen bir referansın ömrünün nerede bittiği, tür bilgisinin nereden tamamlandığı değil.
- Üç kuralın kapsamadığı imzalarda (birden çok girdi,
selfyok) açıklama zorunlu kalıyor; kapsamayan bir imza açıklamasız yazılırsa reddediliyor. - Çıkarım, derleyicinin bir imzadan tek bir doğru okuma çıkardığı durumlarda geçerli; birden fazla eşit ağırlıklı olasılık varsa derleyici bir varsayımda bulunmak yerine yazımı reddediyor.
Sonraki Adım
Bu derste çıkarımın hangi imzaları kapsadığı ve hangilerini kapsamadığı görüldü — kapsayan imzalarda açıklama gereksizdi, kapsamayanlarda hâlâ zorunluydu ve bu zorunluluk işlev parametreleri arasında kuruldu. Ama aynı ihtiyaç bir yapının kendi alanında da ortaya çıkabilir: bir yapı bir referans tutuyorsa, o yapının kendisi referans verdiği veriden daha uzun yaşayamaz ve bu koşulun da bir açıklamayla yazılması gerekir. Sıradaki ders bu koşulu, aynı veriyi bir yapıya taşıyarak ve ödünç vererek tutan iki kurguyu karşılaştırarak ölçüyor.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.