ProgramlamaSistem Programcısı

Hata yönetiminin Result<T, E> üzerinden nasıl gerçekleştiğini ve güvenliği ve kodun okunabilirliğini ihlal etmeden soru işareti operatörünü (?) nasıl doğru kullanacağınızı açıklayın.

Hintsage yapay zeka asistanı ile mülakatları geçin

Cevap.

Rust, hataların açık bir şekilde yönetimi üzerine kuruludur: istisnalar yoktur, bunun yerine Result<T, E> döndürülen bir değer kullanılır. Bu, kodun güvenliğini ve öngörülebilirliğini sağlar.

Konu geçmişi:

Birçok dil istisnalar üzerinde ilerledi, bu da beklenmedik durumlara ve çalışma zamanında istisnaları açıkça işlemeye ihtiyaç duyulmasına yol açtı. Rust, başından beri türler aracılığıyla kontrolü tercih etti, tüm hatalar bir fonksiyonun imzasının parçası olmalıdır.

Sorun:

Ana görev, hataların yok sayılmadığı ve maskelemek yerine açıkça belirtildiği, "çöken" fonksiyonların olmadığı ama kodun yine de kompakt ve okunabilir olduğu bir kod yazmaktır. Hataları yukarı doğru doğru bir şekilde iletmek, tür hakkında bilgi kaybetmemek ve mantığı karıştırmamak gereklidir.

Çözüm:

Ana araç Result<T, E> tipi ve ? operatörüdür, bu otomatik olarak sonucu "çözer": hata durumunda, fonksiyondan hemen çıkış yapılır ve hata geri döner, başarı durumunda ise değer geri döner.

Kod örneği:

fn read_number(file: &str) -> Result<i32, std::io::Error> { let content = std::fs::read_to_string(file)?; let num: i32 = content.trim().parse()?; Ok(num) }

Anahtar özellikler:

  • Tüm potansiyel hataların imzada açıkça belirtilmesi
  • ? operatörü iç içe geçmeyi azaltır (if-let/unwrap/expect kaldırır)
  • hatalar kolayca "sarmalanabilir" veya dönüştürülebilir (.map_err() ve diğer yöntemler aracılığıyla)

İkna edici sorular.

Unit (void) döndüren fonksiyonlarda ? kullanmak mümkün mü?

Hayır, ? operatörü yalnızca Result veya Option döndüren fonksiyonlar içinde geçerlidir. Eğer bir fonksiyon () döndürüyorsa, ? kullanılamaz.

? ile yapılan birkaç çağrıda hata türleri farklı olursa ne olur?

Bir derleme hatası meydana gelir: hata türü açıkça tanımlanmalıdır. Tüm hataları tek bir türde birleştirmek için .map_err() kullanılmalı veya thiserror kullanılmalı ya da API seviyesinde bir enum sarmalayıcı tanımlanmalıdır. Örnek:

fn foo() -> Result<_, MyError> { let a = bar()?; let b = baz().map_err(MyError::Baz)?; Ok() }

İç mantıkta .unwrap() ne kadar tehlikeli?

"Ana" kodda unwrap() kullanılabileceği konusunda yaygın bir yanlış anlama vardır, "kesinlikle çökmez". Gerçekte, görünmeyen küçük bir hata bile çalışma zamanında bir panik ile sonuçlanır — bu da programın güvenliğini tehlikeye atar.

Tipik hatalar ve anti-patentler

  • Tüm hataların unwrap/expect ile yakalanması, özellikle iç mantıkta
  • map_err(|_| …) ile bağlam kaybı, hata orijinal bilgi kaydetmeden kapatıldığında
  • ? olmadan her hatanın manuel olarak işlenmesinde aşırı iç içe geçme

Gerçek yaşam örneği

Negatif vaka

Üretim kodunda hızlı hata ayıklama sonrası birçok unwrap() bırakılmış. Sonuç olarak, uygulama kullanıcıdan gelen hatalı giriş nedeniyle herhangi bir ayrıştırma hatasında çöküyordu.

Artıları:

  • Hızlı hata ayıklama

Eksileri:

  • Uygulamanın çökme riski, sebebini bulmak zor

Pozitif vaka

Tüm hataların açık bir şekilde tanımlandığı ve yalnızca ? ve .map_err() kullanarak tüm hata bilgilerini koruduğu Result<T, E> yığını kullanıldı.

Artıları:

  • Hata ayıklama kolaylığı, öngörülebilir davranış

Eksileri:

  • Biraz daha fazla "gürültülü" hata türü