İçeriğe geç
academia.sh

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.

Aramak için yazmaya başlayın.

↑↓ ↵ Esc gezin · aç · kapat