- Panik ile
Resultarasında doğru seçim yapmak Result'ımatch,unwrap_orveexpectile işlemek- Hatayı
?operatörüyle çağıran fonksiyona iletmek Displayuygulayan kendi hata enum'unu oluşturmak
Kullanıcıya yaşını soruyorsun, o da abc yazıyor. Bir dosyayı okumak istiyorsun ama dosya yok. Bunlar programdaki hatalar değil, beklenen durumlardır; program bunlara çökmeden yanıt vermelidir. Rust, olası tüm başarısızlıkları türlerde göstermeni ister ve derleyici onları unutmana izin vermez.
İki tür hata
Kurtarılamaz bir hata, programda bir bug, yani programcı hatası olduğunu gösterir: dizinin sınırını aşmak ya da “asla olmaması gereken” bir durum. Böyle bir durumda Rust panikler: bir hata mesajı yazdırır ve iş parçacığını durdurur. panic!("mesaj") çağrısını kendin de yapabilirsin. Kurtarılabilir hatalar ise Result<T, E> türüyle döndürülür ve çağıran kod tarafından işlenir.
fn divide(a: i32, b: i32) -> i32 {
if b == 0 {
panic!("division by zero: {a} / {b}");
}
a / b
}
fn main() {
println!("{}", divide(10, 2));
println!("{}", divide(1, 0));
println!("never printed");
}5 thread 'main' panicked at src/main.rs:3:9: division by zero: 1 / 0 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
index out of bounds: the len is 3 but the index is 10.Result türü
Result<T, E>, iki varyantlı bir enum'dur: başarıda Ok(T), başarısızlıkta Err(E). Örneğin metni sayıya çeviren parse metodu Result döndürür, çünkü metin bir sayı olmayabilir. match ile iki durumu ayrı ayrı işleriz:
fn main() {
let inputs = ["42", "abc", ""];
for input in inputs {
match input.parse::<i32>() {
Ok(n) => println!("{input:?} -> {}", n * 2),
Err(e) => println!("{input:?} -> error: {e}"),
}
}
}"42" -> 84 "abc" -> error: invalid digit found in string "" -> error: cannot parse integer from empty string
parse::<i32>() yazımındaki ::<i32> kısmı hangi türe dönüştürüleceğini belirtir (buna “turbofish” denir). Hata {e} ile insanın okuyabileceği bir metin olarak yazdırılır.unwrap, expect ve diğer kısayollar
fn main() {
let a: i32 = "7".parse().unwrap();
let b: i32 = "8".parse().expect("b must be a number");
let c: i32 = "x".parse().unwrap_or(0);
let d: i32 = "".parse().unwrap_or_default();
println!("{}", a + b + c + d);
}15
c ve d için dönüştürme başarısız oldu ama program durmadı: unwrap_or(0) ve unwrap_or_default() yedek bir değer (0) döndürdü. 7 + 8 + 0 + 0 = 15.| Yöntem | Err durumunda | Ne zaman kullanılır |
|---|---|---|
match | her durum için kendi kodun | tam kontrol gerektiğinde |
? | hatayı çağırana döndürür | fonksiyonun kendisi Result döndürdüğünde |
unwrap_or(v) | v döndürür | makul bir yedek değer varsa |
expect("...") | senin mesajınla panik | başarısızlık bir programlama hatası anlamına geliyorsa |
unwrap() | panik | yalnızca örneklerde ve testlerde |
? operatörü
?, bir Result'ın arkasına yazılır. Sonuç Ok ise ? içindeki değeri çıkarır ve kod devam eder. Err ise geçerli fonksiyon o hatayı hemen döndürür. Böylece her adımda match yazmadan hatalar çağıran koda iletilir. Koşul: fonksiyonun kendisi Result (ya da Option) döndürmelidir.
use std::num::ParseIntError;
fn sum_pair(a: &str, b: &str) -> Result<i32, ParseIntError> {
let x: i32 = a.trim().parse()?;
let y: i32 = b.trim().parse()?;
Ok(x + y)
}
fn main() {
println!("{:?}", sum_pair("10", " 32 "));
println!("{:?}", sum_pair("10", "ten"));
match sum_pair("5", "") {
Ok(total) => println!("total = {total}"),
Err(e) => println!("failed: {e}"),
}
}Ok(42)
Err(ParseIntError { kind: InvalidDigit })
failed: cannot parse integer from empty stringtrim() kenarlardaki boşlukları siler. {:?} hatanın iç yapısını, {e} ise okunaklı mesajını gösterir.use std::num::ParseIntError;
fn read_age(s: &str) -> Result<u8, ParseIntError> {
let age = match s.parse::<u8>() {
Ok(value) => value,
Err(e) => return Err(e),
};
Ok(age)
}use std::num::ParseIntError;
fn read_age(s: &str) -> Result<u8, ParseIntError> {
let age = s.parse::<u8>()?;
Ok(age)
}cargo clippy soldaki yazımı görünce onu ? ile değiştirmeyi önerir (question_mark kuralı).fn main() {
let n: i32 = "5".parse()?;
println!("{n}");
}use std::num::ParseIntError;
fn main() -> Result<(), ParseIntError> {
let n: i32 = "5".parse()?;
println!("{n}");
Ok(())
}main hiçbir şey döndürmez; bu yüzden derleyici şunu yazar: the ? operator can only be used in a function that returns Result or Option. main de Result döndürebilir; Err durumunda hata yazdırılır ve program başarısız bir çıkış koduyla biter.Kendi hata türün
Büyük bir programda hataların kendi adları olmalıdır. Hata çeşitlerini bir enum olarak yaz ve ona Display trait'ini uygula ki hata kullanıcıya okunaklı bir mesajla gösterilsin. map_err metodu bir hata türünü başka birine çevirir; genellikle ? ile birlikte kullanılır. (Trait'leri 8. derste ayrıntılı öğreneceksin.)
use std::fmt;
#[derive(Debug)]
enum AgeError {
NotANumber,
TooOld(u32),
}
impl fmt::Display for AgeError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
AgeError::NotANumber => write!(f, "age must be a number"),
AgeError::TooOld(n) => write!(f, "{n} is too old"),
}
}
}
fn parse_age(s: &str) -> Result<u32, AgeError> {
let age: u32 = s.parse().map_err(|_| AgeError::NotANumber)?;
if age > 150 {
return Err(AgeError::TooOld(age));
}
Ok(age)
}
fn main() {
for s in ["15", "abc", "200"] {
match parse_age(s) {
Ok(age) => println!("ok: {age}"),
Err(e) => println!("error: {e} / {e:?}"),
}
}
}ok: 15 error: age must be a number / NotANumber error: 200 is too old / TooOld(200)
{e} bizim Display uygulamamızı, {e:?} ise #[derive(Debug)]'ın ürettiği metni kullanır. |_|, parametresini yok sayan kısa bir fonksiyondur (closure, 7. ders).Önemli noktalar
panic!bir programlama hatasında programı durdurur; beklenen hatalarResult<T, E>ile döndürülür.ResultyaOk(değer)ya daErr(hata)tutar vematchile işlenir.unwrapveexpect,Errdurumunda panikler; üretim kodunda?,matchya daunwrap_ortercih edilir.?,Okdeğerini çıkarır,Err'i ise hemen çağırana döndürür; yalnızcaResultya daOptiondöndüren fonksiyonlarda çalışır.- Kendi hata türün için bir
enumyaz, onaDisplayuygula ve hatalarımap_errile dönüştür.
Kendini test et
10 soru. Her doğru cevap XP kazandırır.
Err olduğunda ? operatörü ne yapar?