Değişkenler ve Değişkenlik

“Değerleri Değişkenlerde Saklamak” bölümünden hatırlayacağınız üzere Rust'ta değişkenler varsayılan olarak değişmez kabul edilmekteydi. Bu kabul kodlarınızı, Rust'ın getirdiği güvenlik ve eşzamanlılık avantajlarından yararlanacak şekilde yazmanızı teşvik eden birçok Rust yaklaşımından biridir. Ancak yine de değişkenlerinizi değişebilir yapma seçeğine her zaman sahipsiniz. Şimdi gelin Rust'ın sizi değişmezliğe nasıl ve neden yönlendirdiğini ve bazen bu değişemezlikten neden vazgeçmemiz gerektiğini birlikte inceleyelim.

Bir değişmez haldeki bir değişkene isim verilerek değer atandığında o değişkenin değerini artık değiştiremezsiniz. Bu konuya açıklık getirebilmek için projeler dizininde cargo new degiskenler komutunu kullanarak degiskenler adında yeni bir proje oluşturalım. Ardından degiskenler dizinindekki src/main.rs dosyasını açarak içindeki kodları şu an için derlenmeyen aşağıdaki kodlarla değiştirelim:

Dosya adı: src/main.rs

fn main() {
    let x = 5;
    println!("x'in değeri: {}", x);
    x = 6;
    println!("x'in değeri: {}", x);
}

Programınızı kaydedip cargo run komutuyla çalıştırdığınızda aşağıdakine gibi bir hata mesajı alacaksınız:

$ cargo run
   Compiling degiskenler v0.1.0 (/home/rustdili/projeler/degiskenler)
error[E0384]: cannot assign twice to immutable variable `x`
 --> src/main.rs:4:5
  |
2 |     let x = 5;
  |         -
  |         |
  |         first assignment to `x`
  |         help: consider making this binding mutable: `mut x`
3 |     println!("x'in değeri: {}", x);
4 |     x = 6;
  |     ^^^^^ cannot assign twice to immutable variable

For more information about this error, try `rustc --explain E0384`.
error: could not compile `no-listing-01-variables-are-immutable` due to previous error

Bu örnek, derleyicinin programlarınızdaki hataları bulmanıza nasıl yardımcı olacağını gösterir. Deneyimli Rust geliştiricilerinin bile karşılaşmaktan muaf olmadığı bu sinir bozucu görünen derleyici hataları, sizin kötü programcı olduğunuzu değil, programınızın yapması gereken şeyleri henüz güvenli bir şekilde gerçekleştiremediğini söylemektedir.

Hata mesajındaki cannot assign twice to immutable variable `x` uyarısı hatanın sebebi olarak değişmez olarak bildirilen x değişkenine ikinci kez değer atanamayacağını ancak bizim x değişkenine yeni bir değer atamaya çalıştığımızı bildirmektedir.

Değişmez olarak belirlenmiş bir değeri değiştirmeye çalışmak programda hatalara neden olabileceğinden böyle bir derleme zamanı hatası almamız önemlidir. Kodumuzun bir bölümünün bir değerin asla değişmeyeceği varsayımıyla hareket ettiği oysa başka bir bölümün bu değeri değiştirdiğini düşündüğünüzde kodun ilk bölümünün tasarlandığı gibi çalışmayacağı ortadadır. Bu şekilde ortaya çıkan hataların kaynağını saptamak, değişken değeri ara sıra değiştirildiğinde daha da çok zorlaşır. Rust'ta bir değerin değişmeyeceğini bildirdiğinizde derleyici bu değerin değişmeyeceğini garanti eder. Bu garanti bir kodu okur veya yazarken, değerlerin nerede ve nasıl değişeceğini takip etmenize gerek olmadığı anlamına gelmekte ve kodlarınızın kolayca anlaşılmasını sağlamaktadır.

Ancak değişebilirlik pratik kod yazmak gibi çok sayıda fayda sağlar. Değişkenler yalnızca varsayılan olarak değişmez olduklarından, tıpkı 2. Bölümde yaptığımız gibi önlerine mut kelimesini ekleyerek onları değişebilirsinizr. Anahtar kelime mut'un eklenmesi ileride bu kodu okuyacaklara, bu değişken değerinin kodun diğer bölümleri tarafından değiştirileceğini de gösterir.

Örneğin src/main.rs dosyasını aşağıdaki şekilde değiştirelim:

Dosya adı: src/main.rs

fn main() {
    let mut x = 5;
    println!("The value of x is: {}", x);
    x = 6;
    println!("The value of x is: {}", x);
}

Programı bu şekilde çalıştırdığımızda aşağıdaki çıktıyı elde ederiz:

$ cargo run
   Compiling degiskenler v0.1.0 (/home/rustdili/projeler/degiskenler)
    Finished dev [unoptimized + debuginfo] target(s) in 0.29s
     Running `target/debug/degiskenler`
x'in değeri: 5
x'in değeri: 6

mut'u kullanmakla x'e bağlı olan 5 değerinin 6 olarak değiştirilmesine izin vermiş oluruz. Hatalardan kaçınmanın yanı sıra verilmesi gereken başka tavizler de vardır. Örneğin, büyük yapılarla çalışırken mevcut bir örneği yerinde değiştirmek, yeni atanacak örneklerin kopyalanarak geri döndürülmesinden daha hızlı olabilir. Yahut küçük veri yapılarıyla çalışılırken yeni örnekler oluşturmak ve işlevsel programlama tarzından daha fazla yararlanmak anlaşılabilirliği arttıracağından, bu netlik uğruna performanstan ödün vermek göze alınabilecek bir tercih olabilir.

Sabitler

Değişmez değişkenler gibi sabitler de bir isme bağlı olan ve değiştirilmesine izin verilmeyen değerlerdir, ancak sabitler ve değişkenler arasında bazı farklılıklar bulunur.

Bunlardan ilki, mut anahtar kelimesinin sabitler ile kullanılmasına izin verilmez. Sabitler sadece varsayılan olarak değil daima değişmez olarak kabul edilirler. Sabitleri let anahtar sözcüğü yerine const anahtar sözcüğü kullanarak bildirebilirsiniz. Bu bildirim sırasında depoladıkları değer türünü açıkça belirtmelisiniz. Türler ve tür ek açıklamaları konusunu bir sonraki konumuz olan Veri Türleri bölümünde inceleyeceğimizden bu konunun ayrıntıları hakkında endişelenmeniz gerekmez. Şimdilik sabitleri bildirdiğiniz esnada türün açıkça belirtilmesi gerektiğini anımsamanız yeterlidir.

İkinci olarak sabitler, küresel kapsam dahil herhangi bir kapsamda bildirilebilirler. Bu da onların, kodun farklı bölümlerinde bilinen değerler olarak kullanılmasını sağlar.

Son olarak sabitler yalnızca bir işlev çağrısı sonucu olmayan sabit bir ifadeye veya çalışma zamanında hesaplanabilen başka bir değere ayarlanabilirler.

Aşağıda bir sabit örneği yer almaktadır:


#![allow(unused)]
fn main() {
const ÜÇ_SAATTEKİ_SANİYELER: u32 = 60 * 60 * 3;
}

ÜÇ_SAATTEKİ_SANİYELER adlı sabit üç saatin içinde kaç saniye olduğu bilgisini tutar. Ve değeri bir dakia içindeki saniye sayısı (60) ile bir saat içindeki dakika sayısı (60) ve saat sayısı olan (3)'ün çarpımına ayarlıdır. Rust'ın sabitler için adlandırma kuralı, kelimele aralarının alt çizgi ile ayrılması ve tüm harflerin büyük olarak kullanılmasıdır. Derleyici derleme zamanında bir dizi işlemi değerlendirebileceğinden, değerin doğrudan 10,800 olarak ayarlanması yerine, anlaşılması ve doğrulaması daha kolay olan biçimde yazılmasına izin verir. Sabit bildiriminde kullanılabilecek işlemler hakkında bilgilenmek için Rust Reference bölümündeki sabit değerlendirme bölümünü inceleyebilirsiniz.

Sabitler, bir programın çalıştığı süre boyunca, bildirildikleri kapsam dahilinde geçerlidir. Bu durum onları, uygulamanızın farklı bölümlerinden erişilebilen, bir oyuncunun alabileceği maksimum puan sayısı veya ışık hızı gibi belirgin değerlerin bilinmesi gerektiğinde oldukça kullanışlı bir seçenek haline getirir.

Programınız genelinde kullanılan sabit olarak kodlanmış değerleri sabit olarak adlandırmak, bu değerin anlamını ileride kodun bakımını üstlenecek geliştiricilere iletmede faydalıdır. Bununla birlikte sabit olarak kodlanmış bir değerin olası bir güncelleme durumunda tek bir yerden değiştirilecek olması kod bakımı için oldukça yararlıdır.

Gölgeleme

Bir önceki "Tahmin Sayısının Gizli Sayı ile Karşılaştırılması" bölümünden hatırlayacağınız üzere daha önce tanımlanmış bir değişken adıyla yeni bir değişken tanımlayabilirsiniz. Rust geliştiricileri tarafından önce tanımlanan değişkenin sonraki tarafından gölgelendiği ifade edilen bu durum, değişkenin kullanılması durumunda ikinci değişkene ait değerin elde edileceği anlamına gelmektedir. Aşağıdaki örnekte gösterildiği gibi, bir değişkeni aynı isimle ve let anahtar kelimesi tekrar kullanarak gölgeleyebiliriz.

Dosya adı: src/main.rs

fn main() {
    let x = 5;

    let x = x + 1;

    {
        let x = x * 2;
        println!("Kapsam içindeki x'in değeri: {}", x);
    }

    println!("x'in değeri: {}", x);
}

Bu program ilk olarak x değişkenini 5 değerine bağlar. Ardından let x = ifadesini tekrarlanması sonucu x değişkenini, x'in orijinal değerini alıp üzerine 1 ekleyerek 6 olacak şekilde gölgeler. Ardından gelen iç kapsamda ise değer üçüncü kez gölgelenerek önceki değer 2 ile çarpılır ve x değişkeni 12 değerini almış olur. İç kapsamdan çıkıldığında içeride yapılmış olan gölgeleme de sona ereceğinden x yeniden 6 değerine döner. Program çalıştırıldığında aşağıdaki çıktıyı verecektir:

$ cargo run
   Compiling degiskenler v0.1.0 (/home/rustdili/projeler/degiskenler)
    Finished dev [unoptimized + debuginfo] target(s) in 0.77s
     Running `target/debug/degiskenler`
Kapsam içindeki x'in değeri: 12
x'in değeri: 6

Gölgeleme, bir değişkeni mut olarak işaretlemekten farklıdır. Bir değişkeni let anahtar kelimesi kullanmadan yeniden atamaya çalışmak derleme zamanı hatasıyla sonuçlanır. Bir değer üzerinde let anahtar kelimesi kullanarak bazı dönüşümler yapabiliyor olsak bile, bu dönüşümler bittiğinde değişken yine bir değişmez olarak kalacaktır.

Gölgeleme ve mut arasındaki bir diğer fark ise let anahtar kelimesini tekrar kullanmakla etkili bir şekilde yeni bir değişken oluşturduğumuzdan, değerin türünü değiştirebilir ve değişkeni aynı adla kullanmaya devam edebiliriz. Örneğin kullanıcılara gösterilecek metinler arasında kaç boşluk olması gerektiğini soran ve girilen bu boşluk değerlerini sayı olarak saklamak istediğimizi düşünelim:

fn main() {
    let boşluk = "   ";     // Üç boşluk
    let boşluk = boşluk.len();
}

İlk boşluk değişkeni string (dizgi), alt satırdaki birinciyle aynı adı taşıyan fakat yepyeni bir değişken olan boşluk değişkeniyse tam sayı türünde olduğundan bu yapıya izin verilir. Gölgelemenin bu avantajı sayesinde boşluk_dizgi ve boşluk_sayı gibi farklı değişkenler oluşturmadan, boşluk adını tekrar kullanmakla bu sorunlardan kurtuluvermiş oluruz. Eğer bunun yerine mut anahtar sözcüğünü aşağıdaki gibi kullanmaya kalkarsak bir derleme zamanı hatası alırız.

fn main() {
    let mut boşluk = "   "; // Üç boşluk
    boşluk = boşluk.len();
}

Hata bize bir değişken türünün değiştirilmesine izin verilmediğini bildiriyor.

$ cargo run                                                                                                                       ✔ 
   Compiling degiskenler v0.1.0 (/home/rusdili/projeler/degiskenler)
error[E0308]: mismatched types
  --> src/main.rs:48:14
   |
47 |     let mut boşluk = "   "; // Üç boşluk
   |                      ----- expected due to this value
48 |     boşluk = boşluk.len();
   |              ^^^^^^^^^^^^ expected `&str`, found `usize`

For more information about this error, try `rustc --explain E0308`.
error: could not compile `degiskenler` due to previous error

Artık değişkenlerin nasıl çalıştığını anladığımıza göre alabilecekleri veri türlerini inceleyebiliriz.