Teknik Detaylar

Mutable ve Immutable Kavramları Nedir?

← Teknik Detaylar
2021-08-17 · 4 dk okuma
Mutable ve Immutable Kavramları Nedir?
Bu yazıyı yapay zekâ ile tartış
Sayfayı kopyala

Seçtiğiniz programlama dilinden bağımsız olarak, kod yazarken en sık karşılaşacağınız temel kavramlardan ikisi Mutable (değiştirilebilir) ve Immutable (değiştirilemez) yapılardır.

Hangi dilin hangi veri tipini nasıl ele aldığını bilmek, özellikle performans optimizasyonu yaparken ve beklenmeyen yan etkileri (side-effects) önlemede kritik bir rol oynar.

💡 Özet (TL;DR):

  • Mutable (Değiştirilebilir): Nesne oluşturulduktan sonra, bellekteki adresi (pointer) değişmeden değerinin değiştirilebildiği veri tipleridir. (Örn: JavaScript Object/Array, Python list/dict, Go slice/map).
  • Immutable (Değiştirilemez): Oluşturulduktan sonra bellekteki değeri doğrudan değiştirilemeyen veri tipleridir. Üzerinde yapılan her değişiklik, bellekte yeni bir adres tahsis edilerek yeni bir kopya oluşturulmasıyla sonuçlanır. (Örn: JavaScript String/Number, Python tuple/str, Go string/int).

Değiştirilemez (Immutable) Değerler Nasıl Çalışır?

"Eğer bir string değiştirilemezse, ben Javascript'te neden bir string değişkenine yeni değer atayabiliyorum?" sorusu programlamaya yeni başlayanların en çok kafasını karıştıran sorudur.

Aşağıdaki JavaScript örneğini inceleyelim:

let text = "Hello";
text = text + " World";

Bu basit kod çalışırken arka planda şu adımlar gerçekleşir:

  1. Bellekte "Hello" değerini içeren bir alan oluşturulur ve text değişkeni bu adresi işaret eder.
  2. text + " World" işlemi yapıldığında "Hello" metni yerinde değiştirilmez.
  3. Bellekte "Hello World" değerine sahip tamamen yeni bir alan oluşturulur.
  4. text değişkeni artık bu yeni adresi işaret etmeye başlar.
  5. Eski "Hello" değeri ise artık hiçbir değişken tarafından işaret edilmediği için çöp toplayıcı (Garbage Collector) tarafından temizlenmeyi bekler.

Eğer bir döngü içinde 10.000 kez string birleştirme yapıyorsanız, aslında arka planda Garbage Collector'ın temizlemesi gereken 9.999 adet çöp nesne üretmiş olursunuz. Bu yüzden yüksek döngülü işlemlerde doğrudan string birleştirmek yerine buffer veya array tabanlı birleştirme yöntemleri tercih edilir.


Değiştirilebilir (Mutable) Değerler ve Yan Etkileri

Mutable veri tiplerinde değer doğrudan bellekteki adresi üzerinde güncellenir. Bu durum bellek tasarrufu sağlasa da, referans paylaşımlarından dolayı beklenmeyen hatalara yol açabilir:

const user1 = { name: "Evren" };
const user2 = user1; // Referans kopyalandı, yeni bir nesne oluşmadı.

user2.name = "Ahmet";

console.log(user1.name); // Çıktı: "Ahmet"

Yukarıdaki örnekte user2 üzerinde yaptığımız değişiklik, user1 nesnesini de doğrudan etkiledi. Çünkü her iki değişken भी bellekte aynı nesne adresini gösteriyordu.


Mutable vs Immutable Karşılaştırması

ÖzellikMutable (Değiştirilebilir)Immutable (Değiştirilemez)
Bellek YönetimiDeğer bellekte yerinde değiştirilir (In-place).Her değişiklik yeni bellek tahsisi (Allocation) gerektirir.
İş Parçacığı GüvenliğiGüvenli değildir (Race condition riski vardır).inherently thread-safe (İş parçacığı güvenlidir).
Yan Etki RiskleriReferans paylaşımlarında kazara veri bozulması yaşanabilir.Yan etki (side-effect) barındırmaz, tahmin edilebilirdir.
En İyi Kullanım SenaryosuYoğun veri modifikasyonu ve döngüsel veri yapıları.Eş zamanlı (concurrency) işlemler, state yönetimi, konfigürasyonlar.

Go (Golang) Dünyasında Mutable ve Immutable

Go dilinde veri yapıları varsayılan olarak değer (value) tipindedir ve fonksiyonlara parametre olarak gönderildiğinde kopyalanırlar. Ancak slice, map ve channel gibi yapılar arka planda birer pointer barındırdığı için mutable gibi davranırlar:

package main

import "fmt"

func updateSlice(s []int) {
    s[0] = 99 // Orijinal slice değerini değiştirir!
}

func main() {
    mySlice := []int{1, 2, 3}
    updateSlice(mySlice)
    fmt.Println(mySlice) // Çıktı: [99, 2, 3]
}

Go'da büyük nesneleri metotlara geçirirken, sürekli kopyalama yapıp Garbage Collector'a yük bindirmemek için nesnelerin kendisi yerine pointer'larını (*MyStruct) parametre olarak geçmek performansı ciddi ölçüde artırır.


Sıkça Sorulan Sorular (FAQ)

JavaScript'teki const anahtar kelimesi nesneleri immutable yapar mı?

Hayır. const sadece değişkenin referans adresinin yeniden atanmasını (reassignment) engeller. Değişkenin işaret ettiği nesne mutable (Object, Array vb.) ise, nesnenin içeriğini değiştirmeye devam edebilirsiniz:

const myArr = [1, 2];
myArr.push(3); // Hata vermez, çalışır!
// myArr = [4, 5]; // Hata verir! (Reassignment engellenir)

Eğer JavaScript'te bir nesneyi tamamen salt-okunur (immutable) yapmak istiyorsanız, sığ düzeyde (shallow) kilitleme sağlayan Object.freeze(obj) metodunu kullanmanız gerekir.

Neden String veri tipi neredeyse tüm dillerde immutable tasarlanmıştır?

  1. Güvenlik: String değerler ağ bağlantılarında, veritabanı bağlantı adreslerinde ve dosya yollarında parametre olarak sıkça kullanılır. String mutable olsaydı, bu kritik veriler süreç içinde değişebilir ve güvenlik açıkları oluşturabilirdi.
  2. Önbellekleme (Interning): Diller bellekte aynı değere sahip string nesnelerinden sadece bir adet tutarak bellek tasarrufu yapabilir (String Pool).
  3. Hash Key Tutarlılığı: String'ler genellikle map/sözlük yapılarında anahtar (key) olarak kullanılır. Değer değişirse hash kodu da bozulurdu.

Modern state yönetimlerinde (React State, Redux) neden immutability zorunludur?

Çünkü state'in değişip değişmediğini anlamanın en hızlı yolu, referans adreslerinin değişip değişmediğini kontrol etmektir (===). Eğer state'i mutable olarak güncellerseniz referans adresi aynı kalacağı için arayüz kütüphaneleri durum değişikliğini algılayamaz (React render tetiklenmez). Bu yüzden state her zaman [...prev, newValue] şeklinde yeni bir kopya oluşturularak güncellenir.