Ders 11 / 15
Panik ve Kurtarılamaz Hatalar
Bir başarısızlığın çağırana hiçbir seçim bırakmadan kalan gövdeyi durdurduğu bir tasarımın, aynı başarısızlığın bir dönüş değeriyle karşılandığı tasarımla yan yana modellenmesi ve iki tasarımın kalan gövde için verdiği sayıların karşılaştırılması.
Yayın Güncelleme
Bu derste
- Kurtarılabilir ve kurtarılamaz iki tasarımı koşturarak kalan gövdenin kaç kez çalıştığını karşılaştırır
- Panik durumunun gerçekten tetiklenmesi gerekmeden, aynı ayrımın nasıl modellenebileceğini açıklar
- Panik seçiminin bir hata türü değil bir tasarım kararı olduğunu örnekle ayırt eder
İçindekiler
Go Temelleri kursu (M09/K01), bir panik durumunu gerçekten tetikleyip bir kurtarma
çağrısıyla yakalayarak panik=true / panik=false biçiminde bir sonuç bastı — panikte
yığıt izi, kurtarmanın kendisinde ise yalnız bir bayrak görünür kılındı. O kurs bu yakalama
düzeneğini kullanabildi, çünkü panik orada tek bir çağrının sınırında, denetimli biçimde
tetiklendi. Bu ders aynı ayrımı hiç tetiklemeden ölçüyor: gerçek bir panik durumu ne
kurulur ne de bir kurtarma çağrısıyla yakalanır. Panik metni ortama bağlı bir veridir —
dosya adı, satır numarası, çağrı yığıtının biçimi çalıştırılan makineye göre değişir — ve
bu ders böyle bir metin basmaz.
Bunun yerine ayrım modellenir: aynı başarısızlık bir kez Result ile karşılanan bir
tasarımda, bir kez de “buradan sonrası koşmaz” biçiminde yazılan bir tasarımda kuruluyor
ve iki tasarımın kalan gövde için verdiği sayılar karşılaştırılıyor. Özel Hata Tipleri
dersi, bir başarısızlığın bir değer olarak taşınabileceğini ve çağıranın bu değerle ne
yapacağına kendisi karar verdiğini gösterdi. Bu ders o kararın reddedildiği bir tasarımı
ölçüyor: çağırana hiçbir seçim bırakmayan bir başarısızlık.
Bu konunun önceki dört dersinde ölçülen her düzenek — yokluk, başarısızlık kolları, kısa
yazım, dönüşüm — bir ortak varsayımı paylaşıyordu: başarısızlık bir değerdi ve program
o değerle birlikte çalışmaya devam ediyordu. Bu son ders o varsayımın sınırını arıyor: her
başarısızlık bu biçimde ele alınabilir mi, yoksa bazı durumlar için “devam et” seçeneğinin
kendisi anlamsız mı? Kurtarılamaz bir durum, tanımı gereği, Result döndürüp çağırana
bırakılamayacak kadar temel bir varsayımın çöktüğü durumdur — ve bu ders bu iki türü
birbirinden ayırmak yerine, ikisinin kalan gövde üzerindeki etkisini sayıyor.
İki Tasarım, Aynı Koşul
Ders başlığındaki iki sözcük — panik ve kurtarılamaz hata — aynı fikrin iki adı. “Panik”, Rust’ın çalışma zamanının bu durumu adlandırma biçimi; “kurtarılamaz hata”, aynı durumun kavramsal adı, yalnız Rust’a özgü değil. Bu ders ikisini de aynı ölçünün altında ele alıyor: bir başarısızlığın, çağırana devam etme seçeneği bırakmadan, kalan işi durdurması.
Aşağıdaki iki koşum aynı geçersiz girdiyi (-5) iki farklı tasarımla ele alıyor. İlkinde
başarısızlık bir Result ile karşılanıyor ve çağıran, hatayı gördükten sonra da kalan
işini sürdürüyor.
// kurtarilabilir.rs — hata bir deger, cagiran devam etmeyi seciyor
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::rc::Rc;
type Defter = Rc<RefCell<BTreeMap<String, u32>>>;
fn say(defter: &Defter, ad: &str) {
*defter.borrow_mut().entry(ad.to_string()).or_insert(0) += 1;
}
fn kere(defter: &Defter, ad: &str) -> u32 {
*defter.borrow().get(ad).unwrap_or(&0)
}
fn dogrula(giris: i32) -> Result<i32, ()> {
if giris < 0 {
return Err(());
}
Ok(giris)
}
fn kalan_is(defter: &Defter) {
say(defter, "kurtarilabilir-devam");
}
fn main() {
let defter: Defter = Rc::new(RefCell::new(BTreeMap::new()));
match dogrula(-5) {
Ok(deger) => println!("gecerli deger: {}", deger),
Err(()) => println!("gecersiz giris karsilandi, program suruyor"),
}
kalan_is(&defter);
println!();
println!("kurtarilabilir-devam: {} kez", kere(&defter, "kurtarilabilir-devam"));
}
gecersiz giris karsilandi, program suruyor kurtarilabilir-devam: 1 kez
HT13. dogrula(-5) bir Err döndürüyor, ama bu dönüş programı durdurmuyor: match
Err kolunu işletiyor, sonra akış olağan biçimde kalan_ise ulaşıyor ve onu çalıştırıyor.
kurtarilabilir-devam bire çıkıyor. Çağıranın elinde bir seçenek vardı — hatayı görüp
devam etmek — ve bu seçeneği kullandı.
Bu tasarımda dogrula başarısızlığını, tam olarak bu konunun önceki dört dersinde ölçülen
düzenekle bildiriyor: Result<i32, ()> dönüyor, Err kolu bir değer taşımıyor (()),
ama varlığı yeterli. mainin gövdesi bu değeri matchle karşılıyor ve iki kolda da
program aynı yere, kalan_ise ulaşıyor. kalan_isin çağrılması matchin bir sonucu değil
— match bittikten sonra, sıradan bir sonraki satır olarak yazılmış. Bu, önceki
derslerde görülen kalıptan bir fark: orada kollar birbirinden bağımsız işler yapıyordu, burada
her iki kol da aynı sonraki adıma çıkıyor, yalnız farklı bir mesaj yazdırarak.
İkinci tasarım aynı koşulu, çağırana bu seçeneği hiç sunmadan ele alıyor.
// kurtarilamaz.rs — kosul saglanmayinca kalan govde tasarim geregi hic cagrilmiyor
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::rc::Rc;
type Defter = Rc<RefCell<BTreeMap<String, u32>>>;
fn say(defter: &Defter, ad: &str) {
*defter.borrow_mut().entry(ad.to_string()).or_insert(0) += 1;
}
fn kere(defter: &Defter, ad: &str) -> u32 {
*defter.borrow().get(ad).unwrap_or(&0)
}
fn devam_isi(defter: &Defter) {
say(defter, "kurtarilamaz-devam");
}
fn main() {
let defter: Defter = Rc::new(RefCell::new(BTreeMap::new()));
let giris = -5;
if giris >= 0 {
println!("gecerli deger: {}", giris);
devam_isi(&defter);
} else {
println!("kurtarilamaz kosul: kalan govde tasarim geregi hic cagrilmiyor");
}
println!("kurtarilamaz-devam: {} kez", kere(&defter, "kurtarilamaz-devam"));
}
kurtarilamaz kosul: kalan govde tasarim geregi hic cagrilmiyor kurtarilamaz-devam: 0 kez
HT14. Aynı koşul (giris < 0), bu kez devam_isinin hiç çağrılmamasıyla sonuçlanıyor.
kurtarilamaz-devam sıfırda kalıyor — kaynakta yazılı, ama bu koşumda hiç çalışmıyor.
İki koşum yan yana konduğunda fark netleşiyor: aynı geçersiz girdi, bir tasarımda kalan
işin çalışmasına, öbüründe hiç çalışmamasına yol açıyor. Sayı, tasarımın kendisinden
kaynaklanıyor — girdinin “ne kadar geçersiz” olduğundan değil.
Bu ikinci tasarımda hiçbir Result yok — mainin kendisi if/else ile karar veriyor
ve devam_isiyi yalnız geçerli dalda çağırıyor. else kolunun kendisi bir mesaj
yazdırıyor, ama bu mesaj bir hata değeri değil, yalnız bir bilgi notu; programın kendisi
bu mesajdan sonra normal biçimde sona eriyor. Bu ders bu ikinci tasarımı gerçek bir
kurtarılamaz durumun modeli olarak sunuyor — gerçek bir kurtarılamaz durum tipik olarak
programı mainin bile bitmesini beklemeden durdurur; burada main olağan biçimde
tamamlanıyor, yalnız devam_isi çağrısı atlanmış oluyor. Model, “kalan işin çalışmaması”
sonucunu taşıyor; programın kendisinin ne zaman ve nasıl sonlandığını taşımıyor.
Bu iki program gerçek bir panik durumunu göstermiyor; ikisi de olağan if/match
dallanmasıyla yazıldı ve ikisi de normal biçimde sonlanıyor. Model, panik durumunun
gerçek düzeneğini (çağrı yığıtının geri sarılması) değil, panik durumunun gözlenebilir
sonucunu (kalan gövdenin hiç çalışmaması) taşıyor. Gerçek bir panik, bu “hiç çalışmama”
durumunu tek bir çağrıda, ara katmanlardaki her işlevi tek tek durdurmadan, otomatik
olarak üretir — bu ders bu otomasyonu kurmuyor, yalnız sonucunu iki elle yazılmış tasarımla
karşılaştırıyor.
Bu modellemenin neden gerekli olduğu, ortama bağlı veri yasağından geliyor. Gerçek bir panik durumu tetiklendiğinde, çalışma zamanı bir metin üretir; bu metin hangi dosyada, hangi satırda durduğunu ve çağrı yığıtının o andaki biçimini içerir. Bu bilgilerin hepsi derleme ortamına, dosya yoluna ve hatta derleyicinin sürümüne bağlıdır — aynı kaynak kodu farklı bir makinede derlenip çalıştırıldığında aynı metni üretmez. Bu kurs boyunca korunan kural (gerçek yol, gerçek bellek adresi, sürüme bağlı çıktı basılmaz) burada da geçerli; bu yüzden panik durumu gerçekten tetiklenmiyor, yerine yalnız sayılabilir olan kısmı — kalan gövdenin çalışıp çalışmadığı — modelleniyor.
Bu modelleme, önceki dört derste ölçülen “derlenmeyen kaynak kod bloğuna konmaz” kuralıyla aynı aileden. Orada derleyicinin reddi düzyazıda tarif ediliyor, kod bloğuna konmuyordu; burada panik durumunun kendisi hiç kurulmuyor, yerine sonucu modelleniyor. İkisinde de ortak olan, ortama bağlı bir çıktının yerine, kaynaktan okunabilen bir sayının konması.
Sınırlayıcı Ölçüm
Yukarıdaki iki tasarım arasındaki fark, hangisinin “doğru” olduğunu söylemiyor. Panik bir hata türü değil, bir tasarım kararıdır — aynı başarısızlık her iki biçimde de yazılabilir ve seçim çağırana ne bırakılacağına bağlıdır.
HT15. Kurtarılabilir tasarım, çağırana bir seçenek bırakıyor: hatayı görmezden gelmek, bir yedek kullanmak, ya da kendi hatasını üretmek. Kurtarılamaz olarak modellenen tasarım ise bu seçeneği baştan kapatıyor — kalan iş, koşul sağlanmadıkça tasarım gereği hiç çalışmıyor ve bunu değiştirmek çağıranın elinde değil. Gerçek bir panik bu ikinci tasarımı bir adım öteye taşır: durdurma yalnız bir işlevin gövdesinde değil, o işlevi çağıran her katmanda, hiçbir açık kod yazılmadan gerçekleşir. Bu ders bu çok katmanlı durdurmayı koşturmuyor; yalnız tek bir katmanda, “hiç çalışmama” sonucunun nasıl modellenebileceğini gösteriyor.
Bu fark, Hata Yayılımının Kısa Yazımı dersinde ölçülen sessiz atlamayla karşılaştırılabilir,
ama aynı şey değil. Orada erken dönüş bir Result üretiyordu; kalan gövde çalışmıyordu,
ama işlev normal biçimde dönüyordu ve çağıran bu dönüşü bir match ile karşılayabiliyordu.
Kurtarılamaz olarak modellenen bu tasarımda da işlev normal biçimde dönüyor — model
gerçek bir panik değil — ama gerçek bir panik bu noktada farklılaşır: normal bir dönüş
üretmez, çağıranın matchle karşılayabileceği bir değer bırakmaz. Bu, kurtarılabilir ve
kurtarılamaz arasındaki asıl çizgi: biri bir değer bırakıyor, öbürü hiçbir değer
bırakmadan akışı kesiyor.
Kurtarılamaz olarak modellenen tasarımda devam_isi tek bir gövde olarak yazıldı, ama
gerçek bir programda kalan iş birden çok adımdan oluşabilir — bir kayıt açma, bir toplam
hesaplama, bir sonucu yazdırma. Bu ders bu adımları tek bir işlevde topladı, ama Hata
Yayılımının Kısa Yazımı dersinde görülen çok adımlı atlama burada da geçerli: giris >= 0
koşulu sağlanmadığında, devam_isinin içindeki bütün adımlar da birlikte atlanır, tek
tek değil. Kalan işin kaç ayrı adımdan oluştuğu önemli değil — koşul sağlanmadığı sürece
hiçbiri çalışmıyor, hepsi birlikte “hiç” sınıfına giriyor.
Bir tasarımcının bu iki yol arasında seçim yapması gereken an, genellikle “bu durum gerçekten oluşabilir mi, oluşursa programın geri kalanı güvenilir kalır mı” sorusuna verilen yanıtla belirleniyor. Bir kullanıcı girdisinin geçersiz olması beklenir bir durumdur — kurtarılabilir tasarım burada doğal bir seçimdir, çünkü çağıran bu durumu önceden tahmin edebilir ve bir yanıtı hazır tutabilir. Buna karşılık, bir programın kendi iç tutarlılığını bozan bir durum — örneğin bir dizinin beklenenden az eleman taşıması, bir kurulumun eksik kalması — genellikle kurtarılamaz olarak ele alınır, çünkü bu durumda geri kalan kodun hangi varsayımlarla yazıldığı artık geçerli değildir ve devam etmek yeni, öngörülemeyen hatalara yol açabilir.
Bu iki uç arasında kesin bir sınır çizmek bu dersin işi değil — sınır, programın kendi
alanına ve tasarımcının kararına bağlı. Bu ders yalnız bu kararın sonucunu sayılabilir
kılıyor: hangi tasarım seçilirse seçilsin, kalan gövdenin çalışıp çalışmadığı, kaynağı
okuyarak değil, koşumu ölçerek doğrulanabilir. kurtarilabilir-devam ile
kurtarilamaz-devam arasındaki 1/0 farkı, bu konunun başından beri sürdürülen ölçünün
son örneği: kaynakta yazılan bir gövde, koşumda kaç kez çalışıyor.
Bir tasarımın kurtarılabilir mi kurtarılamaz mı olacağı, başarısızlığın doğasından
otomatik olarak çıkmaz — bu bir karardır. Aynı geçersiz girdi, bir programda “hatayı
günlüğe yaz, varsayılan değerle devam et” biçiminde ele alınabilir; başka bir programda,
aynı girdi “bu durum asla oluşmamalıydı, devam etmenin bir anlamı yok” biçiminde ele
alınabilir. Rust’ın kendisi bu kararı dayatmaz; Result de olağan işlev dönüşü de aynı
dilin parçasıdır, tercih kaynak kodu yazan tarafa aittir.
Yukarıdaki dogrula işlevinin -5 girdisi, aslında iki tasarımda da “aynı” başarısızlığı
temsil ediyor — ikisinde de kaynak, negatif bir sayının geçersiz sayıldığını söylüyor. Fark
başarısızlığın tespitinde değil, tespit edildikten sonra ne olacağında. Bu, bu konunun
başından beri süren eksenle aynı yerde duruyor: kaynakta yazılan bir gövdenin koşumda kaç
kez çalıştığı. Yalnız bu kez ölçülen gövde bir yedek değer ya da bir dönüşüm değil, bir
işlevin normal akışının geri kalanı — ve bu geri kalanın çalışıp çalışmadığı, doğrudan
tasarımın kendisinden okunuyor.
Özet
- Aynı başarısızlık iki tasarımla yazılabilir: kurtarılabilir tasarımda çağıran kalan işi kendi seçimiyle sürdürür, kurtarılamaz olarak modellenen tasarımda kalan gövde hiç çalışmaz.
- Gerçek bir panik durumu bu derste hiç tetiklenmedi; ayrım, panik metni basılmadan, olağan dallanmayla modellendi — çünkü panik metni dosya, satır ve çağrı yığıtı gibi ortama bağlı veri taşır.
- Kurtarılabilir tasarımda kalan iş çalıştı (
kurtarilabilir-devam: 1), kurtarılamaz olarak modellenen tasarımda çalışmadı (kurtarilamaz-devam: 0) — aynı koşul, farklı sayı. - Panik bir hata türü değil bir tasarım kararıdır; seçim, çağırana ne bırakılacağına bağlıdır — bir durumun beklenir mi yoksa programın iç tutarlılığını mı bozduğuna göre.
- Gerçek bir panik, “hiç çalışmama” sonucunu tek bir çağrıda, ara katmanları tek tek durdurmadan üretir; bu ders bu otomasyonu değil, yalnız gözlenebilir sonucunu ölçtü.
- Kurtarılabilir tasarımda çağıran bir değerle karşılaşır ve karar verir; kurtarılamaz tasarımda hiçbir değer bırakılmaz, akış doğrudan kesilir.
Sonraki Adım
Bu konunun beş dersi boyunca ölçülen gövdeler hep bir işlevin kendi gövdesinin içindeydi — bir işlev çağrıldığında ne kadarının çalıştığı: bir yedek değer, bir kol, bir dönüşüm, bir kalan gövde. Beş ders de aynı üç sınıfa (her seferinde, yalnız gerektiğinde, hiç) yerleşti, yalnız hangi düzeneğin bu sınıflara girdiği değişti. Kapanışlar dersi aynı soruyu, bir gövdenin ne zaman çağrıldığına taşıyor: bir kapanış tanımlandığı anda mı çalışır, yoksa yalnız biri onu çağırdığında mı? Bu konuda ölçülen “kaç kez koştu” sorusu orada da sürüyor, ama artık bir başarısızlık değil, bir tanımın kendisi ölçünün konusu.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.