---
title: İstisnalar
source: 'https://academia.sh/tr/kurslar/ruby-temelleri/istisnalar'
course: 'Ruby Temelleri'
language: tr
updated: '2026-10-21T05:03:43+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'rescue ile bir istisnayı yakalar ve e.class ile türünü okur'
    - "Kendi istisna sınıfını StandardError'dan türetir ve raise ile fırlatır"
    - 'ensure ile kaynak temizleme ve retry ile yeniden deneme yazar'
    - "rescue yapısının sözdizimi, raise'in ise ileti olduğunu ölçer"
---

# İstisnalar

rescue, ensure ve retry yapılarının dilin kendi sözdizimi olduğu, raise'in ise bir ileti olduğu ve rescue dalındaki eşleşmenin bir sınıf iletisiyle yapıldığı ölçülür.

Önceki dersin sonunda bir lambda yanlış sayıda argümanla çağrıldığında `ArgumentError` fırlattı ve bu istisna `rescue` ile yakalandı. Bu ders o yapıyı konu yapar. **İstisna (exception)**, normal akışı kesen bir olaydır; **fırlatma (raise)** onu üretir, **yakalama (rescue)** onu karşılar. M12/K01 PHP'de hata kanallarını ölçmüştü; burada hata kanalı sorusu sorulmuyor, yalnız yapının kim tarafından yanıtlandığı ölçülüyor.

Bu dersin yanıtlayanı **sözdizimi**dir: `rescue` bir yöntem değil, dilin kendi yapısıdır.

## rescue Bir İfadedir

Çekirdek gözlemi `defined?` sorusuna dayanır: bir `begin ... rescue ... end` bloğu için `defined?` ne diyor? Yöntem çağrısı olsaydı `"method"` dönerdi.

```ruby
# yanit.rb — bir iletiyi kimin yanitladigini siniflandiran yardimcilar

# Siniflandirma sirayla sorar: once ifade bir ileti mi (defined?),
# sonra iki farkli blok iki farkli sonuc veriyor mu, sonra yontemin sahibi kim.
def yanitlayan(tanim, sahip: nil, sonuc: nil, bloklu: nil)
  return ["sozdizimi", "defined? #{tanim}"] unless tanim == "method"

  if bloklu
    birinci, ikinci = bloklu.call
    return ["blok", "#{birinci.inspect} / #{ikinci.inspect}"] unless birinci == ikinci
  end

  tur = sahip.instance_of?(Class) ? "sinif" : "modul"
  [tur, "#{sahip}, sonuc #{sonuc.inspect}"]
end
```

```ruby
# kurtarma.rb — rescue yapisini kim yanitliyor
require_relative "yanit"

tanim = defined?(begin; Integer("x"); rescue ArgumentError; 0; end)
tur, kanit = yanitlayan(tanim)
puts "#{tur}  #{kanit}"
deger = Integer("x") rescue 0
puts deger
puts Integer("x", exception: false).inspect
```
```
sozdizimi  defined? expression
0
nil
```

İlk satır çekirdeğin `kurtarma` gözlemidir: yanıtlayan **sözdizimi**, `defined?` `"expression"` verdi. İkinci satır deyim değiştirici biçimi gösterir: `Integer("x") rescue 0` istisnayı yakalayıp `0` verdi. Üçüncü satır aynı işi `exception: false` anahtarlı argümanıyla, istisna fırlatmadan yaptı ve `nil` döndürdü. **KB15.** Bu ders değiştirici `rescue`'ü yalnız `ArgumentError` ile ölçer; başka istisna sınıflarını yakalayıp yakalamadığı ölçülmez.

İkinci satırda `Integer("x") rescue 0` önce bir değişkene atandı, sonra basıldı; değiştirici biçim bir yöntem argümanının ya da `defined?`'ın içine doğrudan yazılmadı. Bu konumlarda nasıl çözüldüğü bu derste ölçülmez.

## İstisna Sınıfları

Her istisna bir sınıfın örneğidir. `e.class` onun türünü verir; iletisi (`e.message`) yerine sınıf adı kullanılır, çünkü ileti metni sürümle değişebilir. Dersin kendi `raise` metni bu kuralın dışındadır.

```ruby
# siniflar.rb — istisna siniflari
def dene
  yield
rescue => e
  e.class
end

puts dene { 1 / 0 }
puts dene { raise "x" }
puts dene { Integer("x") }
puts 1.0 / 0

class StokBitti < StandardError; end

begin
  raise StokBitti, "stok bitti"
rescue StokBitti => e
  puts "#{e.class} #{e.message}"
end
```
```
ZeroDivisionError
RuntimeError
ArgumentError
Infinity
StokBitti stok bitti
```

Sıfıra tam sayı bölmesi `ZeroDivisionError` fırlattı; ondalık bölme ise istisna değil `Infinity` verdi. Düz `raise "x"` bir `RuntimeError` üretti. Kendi sınıfımız `StokBitti`, `StandardError`'dan türedi ve `rescue StokBitti => e` yalnız onu yakaladı. İleti burada dersin kendi `raise` metni olduğu için basılabildi.

## ensure ve retry

`ensure` dalı istisna olsa da olmasa da koşar; kaynak temizleme için kullanılır. `retry` ise `begin` bloğunu baştan çalıştırır.

```ruby
# deneme.rb — ensure ve retry
deneme = 0
begin
  deneme += 1
  raise "henuz degil" if deneme < 3
  puts "basardi #{deneme}"
rescue
  retry
end

olaylar = []
begin
  olaylar << 1
  raise "hata"
rescue
  olaylar << :kurtarildi
ensure
  olaylar << 2
end
puts olaylar.inspect
```
```
basardi 3
[1, :kurtarildi, 2]
```

`retry` her başarısız denemede bloğu yeniden başlattı ve üçüncü denemede iş tamamlandı. İkinci blokta `ensure` dalı, istisna yakalandıktan sonra da koştu. **KB16.** Yeniden deneme sayısı dersin kendi sayacıdır; gerçek bir yeniden deneme senaryosunda sonsuz döngüyü önlemek için bir üst sınır gerekir ve bu burada ölçülmez.

## Dallar ve else

Bir `begin` bloğu birden çok `rescue` dalı taşıyabilir; istisna ilk eşleşen dala gider. `else` dalı yalnız hiçbir istisna olmadığında koşar. Bir sınıfın üst sınıfı `superclass` ile sorulur.

```ruby
# dallar.rb — rescue dallari ve else
def sinifla(deger)
  Integer(deger)
rescue ArgumentError
  "gecersiz"
rescue TypeError
  "tip"
else
  "tamam"
end

puts sinifla("5")
puts sinifla("x")
puts sinifla(nil)
puts ZeroDivisionError.superclass
puts StandardError.superclass
```
```
tamam
gecersiz
tip
StandardError
Exception
```

`"5"` istisna doğurmadığı için `else` dalı koştu. `"x"` `ArgumentError` dalına, `nil` ise `TypeError` dalına gitti: ilk eşleşen dal kazandı. `ZeroDivisionError`'ın üst sınıfı `StandardError`, onun üst sınıfı ise `Exception` çıktı; çıplak `rescue` bu yüzden `ZeroDivisionError`'ı yakalar. **KB18.** Dalların sırası önemlidir: geniş bir sınıf önce yazılırsa ondan sonraki dallar hiç koşmaz, ama bu davranış burada ölçülmez.

## StandardError ve Exception

Parametresiz `rescue` ve `rescue => e` yalnız `StandardError` ve alt sınıflarını yakalar. Hiyerarşide `StandardError`'ın dışında kalan istisnalar bu yakalayıcıdan geçer. Aşağıdaki ölçüm bir `ArgumentError`'ın yakalandığını, bir `NotImplementedError`'ın yakalanmadığını ve ilgili üst sınıfları gösterir.

```ruby
# hiyerarsi.rb — rescue hangi istisnalari yakalar
def sar
  yield
  "yok"
rescue => e
  "yakalandi #{e.class}"
end

puts sar { raise ArgumentError }
begin
  sar { raise NotImplementedError }
rescue NotImplementedError => e
  puts "disarida #{e.class}"
end
puts NotImplementedError.superclass
puts ScriptError.superclass
puts KeyError.superclass
puts IndexError.superclass
```
```
yakalandi ArgumentError
disarida NotImplementedError
ScriptError
Exception
IndexError
StandardError
```

`ArgumentError` yakalandı ve `yakalandi ArgumentError` basıldı. `NotImplementedError` ise `sar`'ın `rescue`'undan geçti ve dıştaki `begin` bloğunda yakalandı. Üst sınıf zinciri bunu açıklar: `NotImplementedError` `ScriptError`'ın, o da `Exception`'ın altındadır; `StandardError`'a ulaşmaz. `KeyError` ise `IndexError` üzerinden `StandardError`'a bağlıdır. **KB34.** Yalnız bu dört sınıfın üst zinciri basıldı.

## ensure Dönüş Değerini Değiştirmez

`ensure` dalı, yöntem nasıl biterse bitsin koşar. Ancak bu dalın son ifadesi yöntemin dönüş değeri olmaz. `return` ile erken çıkıldığında da `ensure` koşar.

```ruby
# ensure_deger.rb — ensure dali ve donus degeri
def deger
  1
ensure
  2
end

def erken
  return 1
ensure
  puts "temizlik"
end

puts deger
puts erken
```
```
1
temizlik
1
```

`deger` `1` döndürdü; `ensure` içindeki `2` dönüş değerini değiştirmedi. `erken` `return 1` ile çıktı, ama `ensure` önce `temizlik` satırını bastı ve ardından `1` döndü. **KB35.** Yalnız bu iki biçim ölçüldü; `ensure` içinde `return` yazılması ölçülmedi.

## Yeniden Fırlatma

`rescue` içinde argümansız `raise`, yakalanan istisnayı olduğu gibi yeniden fırlatır. Böylece bir katman olayı kaydeder, ama kararı üst katmana bırakır.

```ruby
# yeniden.rb — raise ile yeniden firlatma
def disari
  yield
rescue ArgumentError => e
  puts "kayit #{e.class}"
  raise
end

begin
  disari { Integer("x") }
rescue => e
  puts "yeniden #{e.class}"
end
```
```
kayit ArgumentError
yeniden ArgumentError
```

İç `rescue` önce `kayit ArgumentError` satırını bastı ve argümansız `raise` ile aynı istisnayı fırlattı. Dıştaki `rescue` aynı sınıfı gördü: `yeniden ArgumentError`. **KB36.** İstisna iletisi basılmadı; yalnız sınıf ölçüldü.

## retry Sınırı ve Neden Zinciri

`retry` bloğu baştan koşturur; sınırsız bırakılırsa sonsuz döngüye girer. Bu yüzden her zaman bir sayaçla birlikte yazılır. Bir `rescue` içinde yeni bir istisna fırlatılırsa, önceki istisna `cause` ile yeni olanın üzerinde kalır.

```ruby
# sinir.rb — retry sayaci ve cause
deneme = 0
begin
  deneme += 1
  raise "x" if deneme < 10
rescue
  retry if deneme < 3
  puts "vazgecti #{deneme}"
end

begin
  begin
    Integer("x")
  rescue => e
    raise RuntimeError, "ust"
  end
rescue => ust
  puts ust.cause.class
end
```
```
vazgecti 3
ArgumentError
```

İlk iki turda `retry` bloğu yeniden koşturdu; üçüncü turda koşul sağlanmadığı için `vazgecti 3` basıldı. İkinci bölümde `Integer("x")` bir `ArgumentError` fırlattı, `rescue` içinde yeni bir `RuntimeError` fırlatıldı ve dıştaki `ust.cause.class` `ArgumentError` verdi. **KB37.** `retry` yalnız `begin` ve `rescue` biçiminde ölçüldü.

## Çoklu Sınıf ve Dal Sırası

Bir `rescue` birden çok sınıfı virgülle sayabilir. Birden çok `rescue` dalı varsa, ilk eşleşen dal koşar ve sonrakilere bakılmaz.

```ruby
# siralama.rb — rescue listesi ve dal sirasi
def sinifla
  yield
rescue ZeroDivisionError, TypeError
  "ikisi"
rescue StandardError
  "genel"
end
puts sinifla { 1 / 0 }
puts sinifla { 1 + nil }
puts sinifla { Integer("x") }
def ters
  yield
rescue StandardError
  "genel"
rescue ZeroDivisionError
  "ozel"
end
puts ters { 1 / 0 }
```
```
ikisi
ikisi
genel
genel
```

`rescue ZeroDivisionError, TypeError` hem `1 / 0`'ı hem `1 + nil`'i yakaladı: ikisi de `ikisi` basıldı. `Integer("x")` bu listede yoktu ve `genel` dalına düştü. `ters` yönteminde `StandardError` önce yazıldı; `1 / 0` bu dala takıldı ve `ozel` dalına hiç ulaşılmadı. Özel sınıf, genel olandan önce yazılmalıdır. **KB48.** Ölçüm bu üç istisna sınıfıyla yapıldı.

## begin, else ve ensure Sırası

Bir `begin` bloğundaki dallar belirli bir sırayla koşar. Dalların hangi sırayla çalıştığı, bir diziye eklenen sembollerle okunur.

```ruby
# sira.rb — dallarin kosma sirasi
sira = []
begin
  sira << :govde
rescue
  sira << :kurtarma
else
  sira << :else
ensure
  sira << :ensure
end
puts sira.inspect
sira = []
begin
  sira << :govde
  raise "x"
rescue
  sira << :kurtarma
else
  sira << :else
ensure
  sira << :ensure
end
puts sira.inspect
```
```
[:govde, :else, :ensure]
[:govde, :kurtarma, :ensure]
```

Hata yokken `else` gövdeden sonra, `ensure` en son koştu. Hata fırlatılınca `rescue` devreye girdi, `else` atlandı ve `ensure` yine koştu. `ensure` iki durumda da son sırada yer aldı. **KB49.**

## Alanlı İstisna Sınıfı

Kendi istisna sınıfınız ek bilgi taşıyabilir. `initialize` içinde alanı saklar, `super` ile iletiyi verirsiniz.

```ruby
# alanli.rb — kendi alani olan istisna sinifi
class StokHatasi < StandardError
  attr_reader :adet

  def initialize(adet)
    @adet = adet
    super("stok yetersiz")
  end
end

begin
  raise StokHatasi.new(3)
rescue StokHatasi => e
  puts "#{e.class} adet=#{e.adet} ileti=#{e.message}"
end
puts StokHatasi.superclass
puts StokHatasi.instance_method(:adet).owner
```
```
StokHatasi adet=3 ileti=stok yetersiz
StandardError
StokHatasi
```

`StokHatasi` `StandardError`'dan türedi, `adet` alanını taşıdı ve `super` ile kendi ileti metnini verdi. `rescue` alana `e.adet` ile eriştiği için `adet=3` basıldı; `ileti=stok yetersiz` dersin kendi yazdığı metindir. `adet` okuyucusunun sahibi `StokHatasi`'dır: bu yöntemi yanıtlayan sınıftır. **KB50.**

## catch ve throw

`catch` ve `throw` etiketli bir çıkış sağlar. `throw`, aynı etiketle açılmış en yakın `catch` bloğuna kadar çıkar ve ona bir değer taşır.

```ruby
# etiket.rb — catch ve throw
r = catch(:bitti) do
  [1, 2, 3].each { |n| throw :bitti, n * 10 if n == 2 }
  :hic
end
puts r
puts "sahip=#{method(:throw).owner}"
```
```
20
sahip=Kernel
```

`throw :bitti, 20` iç `each` bloğunu ve dıştaki `catch` bloğunu bitirdi; `catch` `:hic` yerine `20` döndürdü. `throw`'un sahibi `Kernel` olarak basıldı. **KB51.** Yalnız bu tek etiketli örnek ölçüldü.

## İç İçe ensure ve rescue Eki

İç bir `begin` bloğunda `ensure` varsa ve istisna orada yakalanmıyorsa, istisna dışarı çıkmadan önce `ensure` koşar. Kısa bir yazım olarak `ifade rescue değer`, ifade hata verirse değeri kullanır; bu yazım parantezle bir değişkene atanabilir.

```ruby
# ic_ice.rb — ic ice ensure ve rescue eki
kayit = []
begin
  begin
    kayit << :ic
    raise ArgumentError
  ensure
    kayit << :temizlik
  end
rescue ArgumentError
  kayit << :dis
end
puts kayit.inspect
kurtarilan = (Integer("x") rescue -1)
puts kurtarilan
```
```
[:ic, :temizlik, :dis]
-1
```

İç blokta istisna yakalanmadı; ama dışa çıkmadan önce `ensure` koştu ve sıra `[:ic, :temizlik, :dis]` oldu. Yani `ensure`, dıştaki `rescue`'dan önce çalışır. `Integer("x")` hata verince ek yazımdaki `-1` kullanıldı. **KB55.** Ek yazımın yalnız `StandardError` ailesini yakaladığı ölçülmedi; bu derste yalnız bu tek örnek denendi.

## Sınırlayıcı Ölçüm

`raise` bir ileti, yani bir yöntem çağrısıdır; `rescue` ise sözdizimidir. İkisinin yanıtlayanı farklıdır. Bir de `rescue ArgumentError => e` dalının kendi içinde bir eşleşme yaptığı sorulabilir: dal, istisnayı `ArgumentError` sınıfına bir iletiyle mi soruyor?

```ruby
# eslesme.rb — rescue dalinin eslesmesi
puts "raise sahibi=#{method(:raise).owner}"

begin
  Integer("x")
rescue ArgumentError => e
  puts ArgumentError === e
  puts "=== sahibi=#{ArgumentError.method(:===).owner}"
end
```
```
raise sahibi=Kernel
true
=== sahibi=Module
```

`raise` bir yöntemdir ve sahibi `Kernel` modülüdür. Dalın eşleşmesi `===` iletisiyle yapılır: `ArgumentError === e` `true` verdi ve `===`'in sahibi `Class` değil `Module`'dür, yani sınıf kendi `===`'ini üst yapıdan alır. Bu, ayrımı netleştirir: `rescue` anahtar sözcüğünü dil çözer, ama hangi dalın istisnayı alacağını sınıfın `===` yanıtı belirler. Dil yapıyı kurar, sınıf eşleşmeyi karar verir. Aynı `rescue` satırında yazılan sınıf bu yüzden sıradan bir sınıf nesnesidir ve kendi eşleşme iletisini yanıtlar. **KB17.** Bu ölçüm yalnız `ArgumentError` dalı için yapıldı; farklı istisna sınıflarının aynı iletiyi aynı sahipten aldığı varsayılmaz.

Sonuç, çekirdeğin sınıflandırmasıyla uyumludur: `defined?` ifadeyi `"expression"` olarak adlandırdı, yani `rescue` anahtar sözcüğü bir ileti değildir; dal eşleşmesi ise yukarıda görüldüğü gibi `===` iletisiyle yapılır. Fırlatan taraf ise farklıdır; `raise` sıradan bir yöntemdir, `Kernel` modülünden gelir ve istisna sınıfını ya da metni argüman olarak alır. Böylece bir istisna olayında iki yanıtlayan yan yana durur: fırlatmayı bir modül, yakalamayı dilin kendisi yanıtlar. Dersin yanıtlayanı olarak **sözdizimi**nin seçilmesinin nedeni de budur: ölçülen soru, yakalama yapısının kim tarafından kurulduğudur.

## Özet

- İstisna, normal akışı kesen bir olaydır; `rescue` yakalar, `raise` fırlatır, `ensure` her durumda koşar, `retry` bloğu yeniden başlatır.
- Bu dersin yanıtlayanı **sözdizimi**dir: `defined?` `begin ... rescue ... end` için `"expression"` döndürür.
- Deyim değiştirici `rescue` ve `exception: false` aynı işi iki yolla görür: istisna yerine bir değer.
- `raise` bir yöntemdir ve sahibi `Kernel` modülüdür; `rescue` dalının eşleşmesi `===` iletisiyle yapılır.
- Sınırlayıcı ölçüm: `rescue` sözdizimidir ama dalının içindeki eşleşme bir sınıf iletisidir.

## Sonraki Adım

Bu derste istisna fırlatan işlemlerin hepsi bellekte çalıştı. Son derste dosyalar ve girdi çıktı gelir: bir dosyayı açan blok mu, yöntem mi, ve `ensure` bu işte hangi yapıyı temizler.
