Değişkenler ve Değiştirilebilirlik (Mutability)
“Değerleri Değişkenlerle Saklamak” bölümünde bahsedildiği gibi, değişkenler varsayılan olarak değiştirilemezdir. Bu, kodunuzu Rust’ın sunduğu güvenlik ve kolay eşzamanlılık (concurrency) avantajlarından yararlanacak şekilde yazmanız için Rust’ın size verdiği birçok küçük dürtmeden biridir. Ancak yine de değişkenlerinizi değiştirilebilir yapma seçeneğiniz vardır. Rust’ın sizi neden değiştirilemezliği tercih etmeye teşvik ettiğini ve neden bazen bundan vazgeçmek isteyebileceğinizi keşfedelim.
Bir değişken değiştirilemez olduğunda, bir değere bir isim bağlandığında,
o değeri bir daha değiştiremezsiniz. Bunu göstermek için,
cargo new degiskenler komutunu kullanarak projects dizininizde
degiskenler adında yeni bir proje oluşturun.
Ardından, yeni degiskenler dizininizde src/main.rs dosyasını açın ve kodunu henüz derlenmeyecek olan şu kodla değiştirin:
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ı kaydedin ve cargo run kullanarak çalıştırın. Aşağıdaki çıktıda gösterildiği
gibi, değiştirilemezlik (immutability) hatasıyla ilgili bir hata mesajı almalısınız:
$ cargo run
Compiling variables v0.1.0 ($PROJE/listings/ch03-common-programming-concepts/no-listing-01-variables-are-immutable)
error[E0384]: cannot assign twice to immutable variable `x`
--> src/main.rs:4:5
|
2 | let x = 5;
| - first assignment to `x`
3 | println!("x'in değeri: {x}");
4 | x = 6;
| ^^^^^ cannot assign twice to immutable variable
|
help: consider making this binding mutable
|
2 | let mut x = 5;
| +++
For more information about this error, try `rustc --explain E0384`.
error: could not compile `variables` (bin "variables") due to 1 previous error
Bu örnek derleyicinin programlarınızdaki hataları bulmanıza nasıl yardımcı olduğunu gösterir. Derleyici hataları sinir bozucu olabilir, ancak aslında yalnızca programınızın sizin istediğiniz şeyi henüz güvenli bir şekilde yapmadığı anlamına gelirler; sizin iyi bir programcı olmadığınız anlamına gelmezler! Deneyimli Rustacean’lar bile hâlâ derleyici hataları almaktadır.
cannot assign twice to immutable variable `x` (x değiştirilemez değişkenine
ikinci kez atama yapılamaz) hata mesajını aldınız çünkü değiştirilemez x değişkenine
ikinci bir değer atamaya çalıştınız.
Değiştirilemez olarak belirlenmiş bir değeri değiştirmeye çalıştığımızda derleme zamanı (compile-time) hataları almamız önemlidir, çünkü bizzat bu durum hatalara (bug) yol açabilir. Kodumuzun bir kısmı bir değerin asla değişmeyeceği varsayımıyla çalışırsa ve kodumuzun başka bir kısmı o değeri değiştirirse, kodun ilk kısmının tasarlandığı şeyi yapmaması mümkündür. Bu tür bir hatanın nedenini oluştuktan sonra takip etmek zor olabilir, özellikle de kodun ikinci parçası değeri yalnızca bazen değiştiriyorsa. Rust derleyicisi, bir değerin değişmeyeceğini belirttiğinizde gerçekten değişmeyeceğini garanti eder, bu nedenle bunu kendiniz takip etmek zorunda kalmazsınız. Böylece kodunuzun mantığını anlamak daha kolaydır.
Ancak değiştirilebilirlik (mutability) çok faydalı olabilir ve kod yazmayı daha
kullanışlı hale getirebilir. Değişkenler varsayılan olarak değiştirilemez olsa da,
Bölüm 2’de yaptığınız gibi değişken
adının önüne mut ekleyerek onları değiştirilebilir yapabilirsiniz. mut
eklemek aynı zamanda kodun diğer kısımlarının bu değişkenin değerini değiştireceğini
belirterek, kodu gelecekte okuyacak kişilere niyetinizi iletir.
Örneğin, src/main.rs dosyasını şu şekilde değiştirelim:
Dosya adı: src/main.rs
fn main() {
let mut x = 5;
println!("x'in değeri: {x}");
x = 6;
println!("x'in değeri: {x}");
}
Şimdi programı çalıştırdığımızda şunu elde ederiz:
$ cargo run
Compiling variables v0.1.0 ($PROJE/listings/ch03-common-programming-concepts/no-listing-02-adding-mut)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.13s
Running `target/debug/variables`
x'in değeri: 5
x'in değeri: 6
mut kullanıldığında x’e bağlı olan değeri 5’ten 6’ya değiştirmemize izin verilir.
Nihayetinde, değiştirilebilirliği kullanıp kullanmamaya karar vermek size bağlıdır ve
o belirli durumda neyin en açık (anlaşılır) olduğunu düşündüğünüze göre değişir.
Sabitleri Bildirmek (Constants)
Değiştirilemez değişkenler gibi, sabitler de bir isme bağlı olan ve değiştirilmesine izin verilmeyen değerlerdir, ancak sabitler ile değişkenler arasında birkaç fark vardır.
İlk olarak, sabitlerle birlikte mut kullanmanıza izin verilmez. Sabitler sadece
varsayılan olarak değiştirilemez değildirler; onlar her zaman değiştirilemezdirler.
Sabitleri let anahtar kelimesi yerine const anahtar kelimesini kullanarak
bildirirsiniz (declare) ve değerin türü mutlaka belirtilmelidir (annotated).
Türleri ve tür bildirimlerini (type annotations) bir sonraki bölüm olan
“Veri Türleri” kısmında ele alacağız, bu yüzden
şu anda ayrıntılar için endişelenmeyin. Sadece her zaman türü belirtmeniz
gerektiğini bilin.
Sabitler, küresel kapsam (global scope) da dahil olmak üzere herhangi bir kapsamda bildirilebilirler; bu durum onları, kodun birçok parçasının bilmesi gereken değerler için kullanışlı hale getirir.
Son fark ise sabitlerin, sadece çalışma zamanında hesaplanabilecek bir değere değil, yalnızca sabit bir ifadeye (constant expression) ayarlanabilmesidir.
İşte bir sabit bildirimi örneği:
#![allow(unused)]
fn main() {
const SANIYE_CINSINDEN_UC_SAAT: u32 = 60 * 60 * 3;
}
Sabitin adı SANIYE_CINSINDEN_UC_SAAT’tir (THREE_HOURS_IN_SECONDS) ve değeri,
60 (bir dakikadaki saniye sayısı), 60 (bir saatteki dakika sayısı) ve
3’ün (bu programda saymak istediğimiz saat sayısı) çarpılması sonucuna ayarlanmıştır.
Rust’ın sabitler için adlandırma geleneği, tüm harfleri
büyük yazmak ve kelimeler arasında alt çizgi kullanmaktır. Derleyici, derleme
zamanında sınırlı bir dizi işlemi değerlendirebilir; bu da sabiti doğrudan
10.800 değerine ayarlamak yerine, bu değeri anlaması ve doğrulaması daha kolay
bir şekilde yazmayı seçmemizi sağlar. Sabitleri bildirirken hangi işlemlerin
kullanılabileceği hakkında daha fazla bilgi için Rust Referansı’nın sabit değerlendirme bölümüne bakın.
Sabitler, bildirildikleri kapsam içinde, program çalıştığı süre boyunca geçerlidirler. Bu özellik, bir oyundaki herhangi bir oyuncunun kazanabileceği maksimum puan sayısı veya ışık hızı gibi, programın birden fazla bölümünün bilmesi gerekebilecek uygulama alanınızdaki (application domain) değerler için sabitleri yararlı kılar.
Programınız boyunca kullanılan sabit (hardcoded) değerleri const (sabit) olarak
adlandırmak, bu değerin anlamını kodun gelecekteki bakımcılarına
iletmek açısından faydalıdır. Ayrıca, sabit kodlanmış değerin gelecekte
güncellenmesi gerektiğinde kodunuzda değiştirmeniz gereken yalnızca bir yer
olmasına da yardımcı olur.
Gölgelendirme (Shadowing)
Bölüm 2’deki tahmin
oyunu eğitiminde gördüğünüz gibi, önceki bir değişkenle aynı isimde yeni bir
değişken bildirebilirsiniz (declare). Rust geliştiricileri (Rustaceans) ilk değişkenin
ikincisi tarafından gölgelendiğini (shadowed) söyler; bu, değişkenin adını
kullandığınızda derleyicinin göreceği şeyin ikinci değişken olduğu anlamına gelir.
Aslında ikinci değişken, kendisi gölgelenene veya kapsam sona erene kadar
değişken adının tüm kullanımlarını kendine alarak ilkini gölgede bırakır (overshadows).
Bir değişkeni aynı değişkenin adını kullanarak ve let anahtar kelimesinin
kullanımını tekrarlayarak aşağıdaki gibi gölgelendirebiliriz:
Dosya adı: src/main.rs
fn main() {
let x = 5;
let x = x + 1;
{
let x = x * 2;
println!("İç kapsamdaki x'in değeri: {x}");
}
println!("x'in değeri: {x}");
}
Bu program önce x’i 5 değerine bağlar. Ardından, let x = komutunu
tekrarlayarak yeni bir x değişkeni oluşturur, orijinal değeri alır ve
1 ekler, böylece x’in değeri 6 olur. Sonra, süslü parantezlerle (curly brackets)
oluşturulan bir iç kapsamda (inner scope), üçüncü let ifadesi de x’i
gölgelendirir ve önceki değeri 2 ile çarparak x’e 12 değerini veren
yeni bir değişken oluşturur. O kapsam sona erdiğinde iç gölgelendirme biter
ve x tekrar 6 olur. Bu programı çalıştırdığımızda şu çıktıyı verecektir:
$ cargo run
Compiling variables v0.1.0 ($PROJE/listings/ch03-common-programming-concepts/no-listing-03-shadowing)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.15s
Running `target/debug/variables`
İç kapsamdaki x'in değeri: 12
x'in değeri: 6
Gölgelendirme, bir değişkeni mut olarak işaretlemekten farklıdır;
çünkü let anahtar kelimesini kullanmadan kazara bu değişkene yeniden atama (reassign)
yapmaya çalışırsak derleme zamanı (compile-time) hatası alırız. let kullanarak,
bir değer üzerinde birkaç dönüştürme (transformation) işlemi gerçekleştirebiliriz, ancak
bu dönüştürmeler tamamlandıktan sonra değişkenin değiştirilemez kalmasını
sağlayabiliriz.
mut ve gölgelendirme arasındaki diğer fark ise şudur: let anahtar kelimesini
tekrar kullandığımızda etkili bir şekilde yeni bir değişken oluşturduğumuz için,
değerin türünü değiştirebilir ancak aynı ismi yeniden kullanabiliriz.
Örneğin, programımızın bir kullanıcıdan biraz metin arasına ne kadar boşluk
istediğini boşluk karakterleri girerek göstermesini istediğini, ve ardından bu
girdiyi bir sayı olarak saklamak istediğimizi varsayalım:
fn main() {
let bosluklar = " ";
let bosluklar = bosluklar.len();
}
İlk bosluklar değişkeni bir metin (string) türüdür ve ikinci bosluklar değişkeni
bir sayı türüdür. Gölgelendirme böylece bizi bosluklar_str ve
bosluklar_sayi gibi farklı isimler bulmak zorunda kalmaktan kurtarır; bunun yerine
daha basit olan bosluklar ismini yeniden kullanabiliriz. Ancak, burada gösterildiği
gibi bunun için mut kullanmaya çalışırsak, derleme zamanı (compile-time) hatası alırız:
fn main() {
let mut bosluklar = " ";
bosluklar = bosluklar.len();
}
Hata, bir değişkenin türünü değiştirmemize (mutate) izin verilmediğini söylüyor:
$ cargo run
Compiling variables v0.1.0 ($PROJE/listings/ch03-common-programming-concepts/no-listing-05-mut-cant-change-types)
error[E0308]: mismatched types
--> src/main.rs:4:17
|
3 | let mut bosluklar = " ";
| ----- expected due to this value
4 | bosluklar = bosluklar.len();
| ^^^^^^^^^^^^^^^ expected `&str`, found `usize`
For more information about this error, try `rustc --explain E0308`.
error: could not compile `variables` (bin "variables") due to 1 previous error
Artık değişkenlerin nasıl çalıştığını keşfettiğimize göre, sahip olabilecekleri daha fazla veri türüne (data types) bakalım.