Taşıma Semantiği

Bir atama (assignment), değişkenler arasında sahipliği (ownership) aktarır:

fn main() {
    let s1 = String::from("Merhaba!");
    let s2 = s1;
    dbg!(s2);
    // dbg!(s1);
}
  • s1’in s2’ye atanması sahipliği (ownership) aktarır.
  • s1 kapsam (scope) dışına çıktığında hiçbir şey olmaz: hiçbir şeye sahip değildir.
  • s2 kapsam (scope) dışına çıktığında, dize (string) verisi serbest bırakılır.

s2’ye taşımadan (move) önce:

StackHeaps1ptrHello!len6capacity6

s2’ye taşıdıktan (move) sonra:

StackDinamiks1ptrMerhaba!len8capacity8s2ptrlen8capacity8BellekHeap(erişilemez)Yıığn

Bir değeri bir fonksiyona geçirdiğinizde, değer fonksiyon parametresine atanır. Bu, sahipliği (ownership) aktarır:

fn say_hello(name: String) {
    println!("Merhaba {name}")
}

fn main() {
    let name = String::from("Ayşe");
    say_hello(name);
    // say_hello(name);
}
This slide should take about 5 minutes.
  • Bunun C++’daki varsayılanların tersi olduğunu belirtin; C++ std::move kullanmadığınız sürece (ve taşıyan yapıcı özel üye fonksiyonu (move constructor) tanımlanmışsa!) değere göre kopyalama yapar.

  • Sadece sahiplik (ownership) taşınır. Verinin kendisini işlemek için herhangi bir makine kodunun üretilip üretilmediği bir optimizasyon meselesidir ve bu tür kopyalamalar agresif bir şekilde optimize edilerek kaldırılır.

  • Basit değerler (tamsayılar gibi) Copy olarak işaretlenebilir (sonraki slaytları inceleyin).

  • Rust’ta, klonlamalar (clones) açıktır (clone kullanarak).

say_hello örneğinde:

  • say_hello’ya yapılan ilk çağrıyla, main name’in sahipliğinden (ownership) vazgeçer. Daha sonra, name artık main içinde kullanılamaz.
  • name için tahsis edilen dinamik bellek (heap), say_hello fonksiyonunun sonunda serbest bırakılacaktır.
  • main, name’i referans olarak (&name) geçirirse ve say_hello parametresinde bir referans kabul ederse sahipliği elinde tutabilir.
  • Alternatif olarak, main ilk çağrıda name’in bir klonunu (name.clone())geçirebilir.
  • Rust, taşıma (move) semantiğini varsayılan yaparak ve programcıları klonlamaları açık hale getirmeye zorlayarak, C++’a göre istemeden kopya oluşturmayı daha zor hale getirir.

Daha Fazlasını Keşfedin

Modern C++’da Savunmacı Kopyalar

Modern C++ bunu farklı şekilde çözer:

std::string s1 = "Cpp";
std::string s2 = s1;  // s1'deki veriyi çoğalt.
  • s1’den gelen dinamik bellek (heap) verisi kopyalanır ve s2 kendi bağımsız kopyasını alır.
  • s1 ve s2 kapsam (scope) dışına çıktığında, her biri kendi belleğini serbest bırakır.

Kopyalayan atama’dan (copy-assignment) önce:

StackHeaps1ptrCpplen3capacity3

Kopyalayan atamadan (copy-assignment) sonra:

StackHeaps1ptrCpplen3capacity3s2ptrCpplen3capacity3

Anahtar noktalar:

  • C++, Rust’tan biraz farklı bir seçim yapmıştır. = veriyi kopyaladığı için, dize (string) verisinin klonlanması gerekir. Aksi takdirde, herhangi bir dize kapsam dışına çıktığında çifte serbest bırakma (double-free) hatası alırdık.

  • C++ ayrıca bir değerin ne zaman taşınabileceğini belirtmek için kullanılan std::move’a sahiptir. Eğer örnek s2 = std::move(s1) olsaydı, hiçbir dinamik bellek (heap) tahsisi gerçekleşmezdi. Taşıma işleminden sonra, s1 geçerli ancak belirsiz bir durumda olurdu. Rust’tan farklı olarak, programcının s1’i kullanmaya devam etmesine izin verilir.

  • Rust’tan farklı olarak, C++’daki = kopyalanan veya taşınan türe göre belirlenen keyfi kodları çalıştırabilir.