Rust'ta Hata Yönetimi 2026: Result, Option, thiserror ve anyhow
Result, Option, ? operatörü ve thiserror ile anyhow kütüphanelerini kullanarak Rust'ta hata yönetimi için kapsamlı rehber. Üretim uygulamalarında kullanılan en iyi pratikler ve kalıplar.

Rust'ta hata yönetimi, derleme zamanında başarı, başarısızlık ve değer yokluğunun açıkça ele alınmasını zorunlu kılan iki enum üzerine kuruludur: Result<T, E> ve Option<T>. Görünmez şekilde yayılan istisnalardan farklı olarak, Rust'ın yaklaşımı hata yollarını fonksiyon imzalarında görünür kılarak çalışma zamanı sürprizlerinin tüm kategorilerini ortadan kaldırır. ? operatörü, thiserror ve anyhow gibi kütüphanelerle birlikte bu açık modeli netliği kaybetmeden basitleştirir.
Option<T> yasal olarak mevcut olmayabilecek değerler için kullanılır (yapılandırma alanları, arama sonuçları). Result<T, E> ise anlamlı hata bilgisiyle başarısız olabilecek işlemler için kullanılır (dosya I/O, ağ istekleri, ayrıştırma).
Result ve Option Temelleri
Result<T, E> başarıyı (Ok(T)) veya başarısızlığı (Err(E)) temsil eder. Option<T> bir değerin varlığını (Some(T)) veya yokluğunu (None) temsil eder. Her ikisi de toplam türlerdir—derleyici her varyantın ele alınmasını sağlar.
// Demonstrates Result and Option basic patterns
fn find_user(id: u64) -> Option<String> {
// Returns None if user doesn't exist
if id == 0 {
None
} else {
Some(format!("User-{}", id))
}
}
fn parse_port(s: &str) -> Result<u16, std::num::ParseIntError> {
// Returns Err if parsing fails
s.parse::<u16>()
}
fn main() {
// Option handling - must address None case
match find_user(42) {
Some(name) => println!("Found: {}", name),
None => println!("User not found"),
}
// Result handling - must address Err case
match parse_port("8080") {
Ok(port) => println!("Port: {}", port),
Err(e) => println!("Invalid port: {}", e),
}
}Derleyici, bu dönüş değerlerini açıkça ele almadan görmezden gelen kodu reddeder. Bu tasarım, diğer dillerde null pointer istisnaları veya yakalanmamış hatalar olarak ortaya çıkacak hataları derleme zamanında yakalar.
Özlü Yayılım için Soru İşareti Operatörü
? operatörü, uzun match zincirlerini okunabilir doğrusal koda dönüştürür. Bir Result üzerine uygulandığında, hata mevcutsa erken dönüş yapar veya başarı değerini açar. Aynı durum Option için de geçerlidir.
// Using ? for clean error propagation
use std::fs::File;
use std::io::{self, BufRead, BufReader};
fn read_first_line(path: &str) -> Result<String, io::Error> {
let file = File::open(path)?; // Returns early if open fails
let mut reader = BufReader::new(file);
let mut line = String::new();
reader.read_line(&mut line)?; // Returns early if read fails
Ok(line.trim().to_string())
}
fn get_port_from_config(path: &str) -> Result<u16, Box<dyn std::error::Error>> {
let content = read_first_line(path)?;
let port = content.parse::<u16>()?; // ParseIntError converts via From
Ok(port)
}? operatörü, hata türünün From trait'i aracılığıyla fonksiyonun dönüş hata türüne dönüştürülebilir olmasını gerektirir. Yukarıda gösterildiği gibi Box<dyn std::error::Error> kullanmak, standart Error trait'ini uygulayan herhangi bir hata türünü kabul eder.
thiserror ile Özel Hatalar Oluşturma
thiserror kütüphanesi, özel hata türleri için standart kod yazma ihtiyacını ortadan kaldırır. Prosedürel bir makro aracılığıyla Error, Display ve From implementasyonlarını türetir.
// Custom error types using thiserror 2.0
use thiserror::Error;
#[derive(Error, Debug)]
pub enum ConfigError {
#[error("configuration file not found at {path}")]
NotFound { path: String },
#[error("invalid port number: {0}")]
InvalidPort(#[from] std::num::ParseIntError),
#[error("IO error reading config")]
IoError(#[from] std::io::Error),
#[error("missing required field: {0}")]
MissingField(String),
}
fn load_config(path: &str) -> Result<Config, ConfigError> {
let content = std::fs::read_to_string(path)?; // IoError auto-converts
let port: u16 = content
.lines()
.find(|l| l.starts_with("port="))
.ok_or(ConfigError::MissingField("port".into()))?
.strip_prefix("port=")
.unwrap()
.parse()?; // ParseIntError auto-converts to InvalidPort
Ok(Config { port })
}
struct Config {
port: u16,
}#[from] özelliği otomatik dönüşümler oluşturarak farklı kaynak hata türleriyle ? operatörünün sorunsuz kullanımını sağlar. Hata mesajları #[error(...)] format dizeleri aracılığıyla kendi kendini belgeler hale gelir.
anyhow ile Uygulama Düzeyinde Hatalar
thiserror belirli hata türleriyle kütüphane koduna uygun olsa da, anyhow tür ayrıntısından çok hata bağlamının önemli olduğu uygulamaları hedefler. Context trait'i herhangi bir hataya açıklayıcı mesajlar ekler.
// Application error handling with anyhow 1.0
use anyhow::{Context, Result, bail, ensure};
fn load_database_url() -> Result<String> {
std::env::var("DATABASE_URL")
.context("DATABASE_URL environment variable not set")
}
fn connect_to_database(url: &str) -> Result<DatabaseConnection> {
ensure!(!url.is_empty(), "database URL cannot be empty");
let conn = DatabaseConnection::new(url)
.context("failed to establish database connection")?;
if !conn.is_healthy() {
bail!("database connection unhealthy after establishment");
}
Ok(conn)
}
fn main() -> Result<()> {
let url = load_database_url()?;
let conn = connect_to_database(&url)
.context("application startup failed")?;
// Context chains create readable error traces:
// Error: application startup failed
// Caused by:
// 0: failed to establish database connection
// 1: connection refused
Ok(())
}
struct DatabaseConnection;
impl DatabaseConnection {
fn new(_url: &str) -> Result<Self> { Ok(Self) }
fn is_healthy(&self) -> bool { true }
}context() metodu hataları ek bilgilerle sarmalayarak hata ayıklamaya yardımcı bir zincir oluşturur. bail! makrosu biçimlendirilmiş bir hata mesajıyla erken çıkış sağlarken, ensure! panik yapmak yerine hata döndüren bir doğrulama görevi görür.
Rust mülakatlarında başarılı olmaya hazır mısın?
İnteraktif simülatörler, flashcards ve teknik testlerle pratik yap.
Gerçek Projelerde thiserror ve anyhow'u Birleştirme
Kütüphaneler, tüketiciler tarafından programatik işleme için thiserror aracılığıyla yapılandırılmış hatalar sunar. Uygulamalar bu hataları insan tarafından okunabilir çıktı için anyhow ile sarmalar. Bu ayrım, hata ayıklama yeteneğini korurken API'leri temiz tutar.
// Exposes typed errors for programmatic handling
use thiserror::Error;
#[derive(Error, Debug)]
pub enum PaymentError {
#[error("insufficient funds: required {required}, available {available}")]
InsufficientFunds { required: u64, available: u64 },
#[error("card declined: {reason}")]
CardDeclined { reason: String },
#[error("payment provider unavailable")]
ProviderUnavailable(#[source] reqwest::Error),
}
pub fn process_payment(amount: u64) -> Result<Receipt, PaymentError> {
// Library returns specific, matchable error types
Err(PaymentError::InsufficientFunds {
required: amount,
available: 50,
})
}
pub struct Receipt;// Wraps library errors with context
use anyhow::{Context, Result};
use my_payment_lib::{process_payment, PaymentError};
fn checkout(cart_total: u64) -> Result<()> {
match process_payment(cart_total) {
Ok(_receipt) => Ok(()),
Err(PaymentError::InsufficientFunds { required, available }) => {
// Handle specific case differently
println!("Add {} to your balance", required - available);
Ok(())
}
Err(e) => Err(e).context("checkout payment processing failed"),
}
}Bu kalıp, kurtarma mümkün olduğunda çağıranların belirli varyantları eşleştirmesini sağlarken, başarısızlıkları yukarı doğru yayarken zengin hata bağlamından da yararlanır. Rust topluluğu bu yaklaşımı büyük ölçüde standartlaştırmıştır, bu konu Rust API kılavuzlarında tartışılmaktadır.
Asenkron Kod için Hata Yönetimi Kalıpları
Asenkron fonksiyonlar tıpkı senkron olanlar gibi Result döndürür. ? operatörü async blokları içinde aynı şekilde çalışır ve hem thiserror hem de anyhow değişiklik olmadan entegre olur.
// Error handling in async Rust with Tokio
use anyhow::{Context, Result};
use std::time::Duration;
async fn fetch_user_data(user_id: u64) -> Result<UserData> {
let response = reqwest::get(format!("https://api.example.com/users/{}", user_id))
.await
.context("HTTP request to user API failed")?;
let status = response.status();
if !status.is_success() {
anyhow::bail!("user API returned status {}", status);
}
let data: UserData = response
.json()
.await
.context("failed to parse user data JSON")?;
Ok(data)
}
async fn fetch_with_retry(user_id: u64, attempts: u32) -> Result<UserData> {
let mut last_error = None;
for attempt in 1..=attempts {
match fetch_user_data(user_id).await {
Ok(data) => return Ok(data),
Err(e) => {
last_error = Some(e);
if attempt < attempts {
tokio::time::sleep(Duration::from_millis(100 * attempt as u64)).await;
}
}
}
}
Err(last_error.unwrap()).context(format!("failed after {} attempts", attempts))
}
#[derive(serde::Deserialize)]
struct UserData {
name: String,
}Birden fazla async işlemi birleştirirken, bunları eşzamanlı çalıştırırken ilk hatayı yaymak için tokio veya futures'dan try_join! kullanılmalıdır.
Downcasting ve Hata İncelemesi
Hem anyhow::Error hem de Box<dyn Error>, orijinal hata türünü kurtarmak için downcasting'i destekler. Bu, genel hataları yayarken belirli ayrıntıları günlüğe kaydetmeyi mümkün kılar.
// Inspecting wrapped error types
use anyhow::{Context, Result};
use std::io;
fn log_and_propagate(result: Result<()>) -> Result<()> {
if let Err(ref e) = result {
// Check if the root cause is a specific type
if let Some(io_err) = e.downcast_ref::<io::Error>() {
match io_err.kind() {
io::ErrorKind::NotFound => {
tracing::warn!("file not found, using defaults");
}
io::ErrorKind::PermissionDenied => {
tracing::error!("permission denied - check file ownership");
}
_ => {
tracing::error!("IO error: {:?}", io_err);
}
}
}
}
result
}Downcasting, genel hata yönetimi ile belirli kurtarma mantığı arasında köprü kurar. Dikkatli kullanılmalıdır—sık downcasting gerçekleşiyorsa, türlendirilmiş bir hata enum'unun daha iyi hizmet edip edemeyeceği değerlendirilmelidir.
Option ve Result Arasında Dönüştürme
Standart kütüphane, Option ve Result arasında dönüştürme için metotlar sağlayarak farklı API'ler farklı kalıplar kullandığında sorunsuz kompozisyon sağlar.
// Option and Result interoperability
fn get_env_port() -> Option<u16> {
std::env::var("PORT")
.ok() // Result -> Option (discards error)
.and_then(|s| s.parse().ok())
}
fn get_env_port_with_error() -> Result<u16, String> {
std::env::var("PORT")
.map_err(|_| "PORT not set".to_string())?
.parse()
.map_err(|_| "PORT is not a valid number".to_string())
}
fn lookup_and_parse(map: &std::collections::HashMap<String, String>, key: &str) -> Result<u16, String> {
map.get(key)
.ok_or_else(|| format!("key '{}' not found", key))? // Option -> Result
.parse()
.map_err(|e| format!("parse error for '{}': {}", key, e))
}ok() metodu yalnızca varlığın önemli olduğu durumlarda hata ayrıntılarını atar. ok_or() ve ok_or_else() metotları None'u özel bir hataya dönüştürerek Option değerlerinden ? yayılımını mümkün kılar.
Performans Değerlendirmeleri
Rust'ta hata yönetimi, başarı yolunda çalışma zamanı maliyeti taşımaz. Result ve Option, deterministik boyutta yığın üzerinde tahsis edilen enum'lardır. Derleyici, bir dalın erişilemez olduğunu kanıtlayabildiğinde kontrolleri optimize eder.
| Yaklaşım | Başarı Yolu Maliyeti | Hata Yolu Maliyeti | |----------|------------------|-------------------| | Result/Option | Sıfır | Yığın çözme (ucuz) | | panic! | Sıfır | Tam yığın çözme + temizlik | | C++ istisnaları | Sıfır (genellikle) | Pahalı heap tahsisi + RTTI |
Kütüphane kodunda unwrap() ve expect() kullanımından kaçınılmalıdır—bunlar yalnızca başarısızlığın gerçekten bir hata gösterdiği durumlar için ayrılmalıdır. Hataların yaygın olduğu performans kritik yollarda, heap üzerinde tahsis edilen hata türleri yerine satır içi verili enum'lar düşünülmelidir.
Sonuç
Result<T, E>kurtarılabilir başarısızlıkları ele alır;Option<T>yokluğu ele alır—her ikisi de derleme zamanı işlemeyi zorlar?operatörü hataları özlü bir şekilde yayar, tür dönüşümü içinFromtrait implementasyonları gerektirirthiserrorkütüphaneler için sıfır standart kodla yapılandırılmış hata türleri oluştururanyhowuygulamalar içincontext(),bail!veensure!destekleyen bağlamsal hata zincirleri sağlar- Her ikisi birleştirilmelidir: kütüphaneler
thiserroraracılığıyla türlendirilmiş hatalar sunar, uygulamalar bunlarıanyhowile sarmalar - Async kod aynı kalıpları kullanır—
?async bloklarda değişiklik olmadan çalışır Option'ıResult'a dönüştürmek içinok_or()kullanılır; tersi içinok()kullanılır- Sarmalanmış hatalar yalnızca belirli kurtarma mantığı orijinal türü gerektirdiğinde downcast edilmelidir
Pratik yapmaya başla!
Mülakat simülatörleri ve teknik testlerle bilgini test et.
Etiketler
Paylaş
İlgili makaleler

Rust Akıllı İşaretçiler: Box, Rc, Arc ve RefCell (2026)
Rust akıllı işaretçileri Box, Rc, Arc ve RefCell; derlenebilir 2026 örnekleri, bir karar tablosu ve yaygın mülakat sorularıyla açıklanıyor.

Rust 2026: Trait, Jenerik Yapılar ve İleri Düzey Mülakat Soruları
Rust trait ve jenerik sistemindeki en güncel değişiklikler: trait upcasting, AsyncFn, RPITIT, use<> söz dizimi. Derlenebilir kod örnekleri ve ileri düzey mülakat soruları içerir.

Rust'ta Async/Await: Tokio, Futures ve Asenkron Eszamanlilik Rehberi
Rust'ta async/await ile asenkron programlama rehberi. Tokio runtime, Future trait, channel yapilari, semaphore ile hiz sinirlandirma, spawn_blocking ve hata yonetimi. Uretim ortamina yonelik kod ornekleri ve mulakat sorularina hazirlik.