Rust'ta değişkenler let ile tanımlanır ve varsayılan olarak değişmezdir (immutable): bir isme değer bağladıktan sonra o değeri değiştiremezsiniz. Değiştirmek istiyorsanız let mut yazarsınız. Aynı ismi yeni bir let ile tekrar tanımlamak ise gölgeleme (shadowing) olur ve tipi bile değiştirebilir. Programın her yerinde geçerli, hiç değişmeyen değerler için const, tek bir bellek adresine sahip global değerler için static kullanılır.
Bu yazıda bu beş kavramı, aralarındaki farkları ve derleyicinin her biri için verdiği gerçek hata ve uyarı mesajlarını Rust 1.99.1 çıktılarıyla gösteriyoruz. Ayrıca Rust 2024 edition'ın static mut konusunda getirdiği sıkılaştırmayı ve sonradan ilk değer verme kurallarını ele alıyoruz. Kurulum yapmadıysanız önce Rust kurulumu yazısına, serinin bütününe Rust öğrenme rehberi sayfasından bakabilirsiniz.
let ve varsayılan değişmezlik

Aşağıdaki program x değişkenine önce 5 bağlıyor, sonra 6 atamaya çalışıyor. Çoğu dilde sorunsuz çalışacak bu kod Rust'ta derlenmez:
fn main() {
let x = 5;
println!("x: {x}");
x = 6;
println!("x: {x}");
}
// error[E0384]: cannot assign twice to immutable variable `x`
// help: consider making this binding mutableDerleyici yalnızca hatayı göstermekle kalmaz, ilk atamanın yerini işaretler ve mut eklemenizi önerir. The Book bu tasarımı şöyle gerekçelendirir: kodun bir bölümü bir değerin hiç değişmeyeceğini varsayarken başka bir bölüm onu değiştirirse, ilk bölüm beklendiği gibi çalışmayabilir ve bu tür hataların kaynağını sonradan bulmak zordur, özellikle değer yalnızca bazen değişiyorsa. Rust, bir değerin değişmeyeceğini söylediğinizde bunun gerçekten öyle kalacağını derleme zamanında garanti eder; siz de bunu takip etmek zorunda kalmazsınız.
Değişmezlik Rust'ın güvenli eşzamanlılık hedefiyle de ilgilidir. Değişmeyen bir değeri birden çok iş parçacığının okuması sorun yaratmaz; sorun, değerin aynı anda hem okunup hem değiştirilmesidir. Rust bu kuralı daha genel hâliyle ödünç alma (borrowing) sisteminde de uygular; ayrıntılar referanslar ve borrowing yazısında.
mut ile değiştirilebilir değişken
mut anahtar kelimesi değişkeni değiştirilebilir yapar ve kodu okuyan kişiye bu değerin ileride değişeceğini haber verir. Derleyici, mut ile işaretlediğiniz ama hiç değiştirmediğiniz değişkenler için uyarı verir. Aynı şekilde tanımlayıp hiç kullanmadığınız değişkenler için de uyarı alırsınız; isim alt çizgiyle başlıyorsa bu uyarı verilmez:
fn main() {
let mut sayac = 0;
sayac += 1;
sayac += 1;
println!("sayaç: {sayac}");
let mut gereksiz = 10; // uyarı: variable does not need to be mutable
println!("gereksiz: {gereksiz}");
let kullanilmayan = 3; // uyarı: unused variable: `kullanilmayan`
let _bilerek = 4; // alt çizgi: uyarı yok
}
// çıktı: sayaç: 2
// çıktı: gereksiz: 10Bu uyarılar, rustc'nin varsayılan olarak açık unused_mut ve unused_variables lint'lerinden gelir. Uyarı programın derlenmesini engellemez ama çoğu zaman bir mantık hatasına işaret eder: değiştirmeyi planladığınız bir değişkeni unuttuğunuzu ya da yanlış değişkeni değiştirdiğinizi gösterebilir. cargo fix komutu gereksiz mut gibi açık çözümü olan uyarıları otomatik düzeltebilir.
Fonksiyon parametreleri de varsayılan olarak değişmezdir. Reference'a göre mut yalnızca hemen ardından gelen parametreye uygulanır. Aynı kural desenle tanımlanan değişkenler için de geçerlidir: bir tuple'ı parçalarken bazı parçaları değişmez, bazılarını değiştirilebilir yapabilirsiniz. Desenlerin tamamı için pattern söz dizimi yazısına bakın.
fn yasi_artir(mut yas: u32) -> u32 {
yas += 1;
yas
}
fn main() {
let (ad, mut yas) = ("Ayşe", 30);
yas += 1;
println!("{ad} {yas}");
println!("{}", yasi_artir(yas));
println!("{yas}");
}
// çıktı: Ayşe 31
// çıktı: 32
// çıktı: 31Son satırda yas hâlâ 31'dir: u32 kopyalanan bir tip olduğu için fonksiyon kendi kopyasını değiştirdi. Parametredeki mut, çağıran tarafın değişkenini değil, fonksiyonun içindeki yerel kopyayı değiştirilebilir yapar. Çağıranın değişkenini değiştirmek için &mut referansı gerekir.
Sonradan ilk değer verme
let ile bir değişkeni değer vermeden tanımlayıp ilk değerini daha sonra atayabilirsiniz. Bu, değişkenin mut olmasını gerektirmez; çünkü bu bir yeniden atama değil, ilk atamadır. Reference'a göre yerel değişkenler, ulaşılabilir bütün kontrol akışı yollarında ilk değerini almadan kullanılamaz:
fn main() {
let not: u32 = 72;
let durum;
if not >= 50 {
durum = "geçti";
} else {
durum = "kaldı";
}
println!("{durum}");
}
// çıktı: geçtielse dalını silerseniz durum bazı yollarda ilk değerini almadan kullanılmış olur ve derleyici reddeder. Mesajın bir notu ilginçtir: derleyici dalların koşullarını değerlendirmez, yalnızca olası yolları inceler. Yani not değeri 72 olarak sabit olsa bile hata verilir:
// else dalı olmadan:
// error[E0381]: used binding `durum` is possibly-uninitialized
// note: when checking initialization, the compiler describes possible control-flow paths
// without evaluating whether branch conditions can actually have the values shownPratikte bu kalıp yerine çoğu zaman let durum = if not >= 50 { "geçti" } else { "kaldı" }; yazılır, çünkü if Rust'ta bir ifadedir ve değer üretir. Bu konu kontrol akışı yazısında ayrıntılı işleniyor.
Shadowing (gölgeleme)

Aynı isimde yeni bir değişkeni yeniden let ile tanımladığınızda ilk değişken gölgelenir: o noktadan sonra derleyici bu ismi gördüğünde yeni değişkeni kullanır. Gölgeleme, iç kapsamda yapılırsa yalnızca o kapsam bitene kadar sürer:
fn main() {
let x = 5;
let x = x + 1;
{
let x = x * 2;
println!("iç kapsamda x: {x}");
}
println!("dış kapsamda x: {x}");
let bosluklar = " ";
let bosluklar = bosluklar.len();
println!("boşluk sayısı: {bosluklar}");
}
// çıktı: iç kapsamda x: 12
// çıktı: dış kapsamda x: 6
// çıktı: boşluk sayısı: 3Program önce x'e 5 bağlar, sonra yeni bir x oluşturup 6 yapar. Süslü parantezle açılan iç kapsamda üçüncü bir x değeri 12'ye çıkarır; kapsam bitince bu gölgeleme sona erer ve x yeniden 6 olur. Son iki satır ise gölgelemenin asıl gücünü gösterir: ilk bosluklar bir metin (&str), ikincisi bir sayıdır (usize). Böylece bosluklar_str ve bosluklar_sayi gibi ayrı isimler uydurmak zorunda kalmazsınız.
Aynı şeyi mut ile yapmaya çalışırsanız derleyici izin vermez, çünkü mut değeri değiştirir ama tipi değiştiremez:
fn main() {
let mut bosluklar = " ";
bosluklar = bosluklar.len();
println!("{bosluklar}");
}
// error[E0308]: mismatched types
// expected `&str`, found `usize`| Özellik | mut | Shadowing |
|---|---|---|
| Yeni değişken oluşturur mu | Hayır, aynı değişken | Evet, aynı isimde yeni değişken |
| Tip değişebilir mi | Hayır | Evet |
| Sonradan değiştirilebilir mi | Evet | Yalnızca yeni değişken mut ise |
| let gerekir mi | Hayır, düz atama | Evet, her seferinde let |
| Kapsam bitince | Değişken kapsamla birlikte biter | Önceki değişken yeniden görünür |
Gölgelemenin güvenlik açısından bir avantajı vardır: bir değeri birkaç adımda dönüştürüp sonra değişmez bırakabilirsiniz. Dönüşümler bittikten sonra yanlışlıkla let olmadan atama yaparsanız derleme hatası alırsınız. Dezavantajı ise uzun fonksiyonlarda aynı ismin farklı anlamlar taşımasının okumayı zorlaştırabilmesidir; gölgelemeyi bir değerin dönüştürülmüş hâli için kullanmak iyi bir alışkanlıktır.
const: derleme zamanı sabitleri
Sabitler de değişmez değişkenler gibi bir isme bağlı ve değiştirilemeyen değerlerdir, ama birkaç önemli fark vardır. const ile mut kullanılamaz; sabitler her zaman değişmezdir. Tip belirtmek zorunludur. Sabitler global kapsam dahil her kapsamda tanımlanabilir. Değerleri yalnızca derleme zamanında hesaplanabilen bir sabit ifade olabilir; çalışma zamanında hesaplanan bir sonuç sabite atanamaz. İsimlendirme geleneği, kelimeler arasında alt çizgi olan büyük harflerdir:
const SAATTE_SANIYE: u32 = 60 * 60;
const UC_SAAT: u32 = SAATTE_SANIYE * 3;
static SITE_ADI: &str = "keydal.tr";
fn main() {
const YEREL_LIMIT: usize = 3;
println!("{UC_SAAT} saniye");
println!("{SITE_ADI}");
let dizi = [0u8; YEREL_LIMIT];
println!("{dizi:?}");
}
// çıktı: 10800 saniye
// çıktı: keydal.tr
// çıktı: [0, 0, 0]Derleyici sınırlı bir işlem kümesini derleme zamanında değerlendirebilir; bu yüzden 10800 yazmak yerine 60 * 60 * 3 yazarak değerin nereden geldiğini okunur kılabilirsiniz. Sabitler başka sabitlere de başvurabilir. Örnekteki YEREL_LIMIT bir fonksiyonun içinde tanımlanmış ve dizi uzunluğu olarak kullanılmıştır; dizi uzunlukları derleme zamanında bilinmek zorunda olduğu için burada let ile tanımlanmış bir değişken kullanamazdınız. Tipi yazmayı unutursanız derleyici bir öneriyle birlikte reddeder:
const AZAMI_PUAN = 100;
// error: missing type for `const` item
// help: provide a type for the constant: `: i32`Reference'a göre sabitler kullanıldıkları her yere satır içi olarak kopyalanır; aynı sabite yapılan referansların aynı bellek adresini göstermesi garanti edilmez. Programın uygulama alanındaki sabit değerlere (bir oyuncunun alabileceği azami puan, ışık hızı gibi) isim vermek, değerin anlamını koda yansıtır ve değer değişirse tek bir yeri güncellemeniz yeter.
static: tek adresli global değerler

static öğeler sabitlere benzer, ancak Reference'a göre programda bir bellek alanını temsil ederler ve o static'e yapılan bütün referanslar aynı alanı gösterir. Static öğeler 'static ömrüne sahiptir, yani program boyunca yaşarlar ve program sonunda drop çağrılmaz. Değişmez bir static'in tipi iş parçacıkları arasında güvenle paylaşılabilmesi için Sync trait'ini uygulamalıdır. Reference, genel olarak const'u tercih etmenizi, static'i ise büyük miktarda veri saklarken, tek adres özelliğine ihtiyaç duyduğunuzda ya da iç değişebilirlik (interior mutability) gerektiğinde kullanmanızı önerir.
static mut ile değiştirilebilir bir global değer de tanımlanabilir, ama Reference'a göre bu tür değerleri okumak da yazmak da unsafe blok gerektirir; çünkü iki iş parçacığının aynı anda değiştirmesi veri yarışına yol açabilir. Rust 2024 edition bu konuda bir adım daha atmıştır: Edition Guide'a göre static mut değerine referans almayı yakalayan static_mut_refs lint'i 2024 edition'da varsayılan olarak deny seviyesindedir. Görünürde referans almasanız bile println! gibi makrolar gizlice referans aldığı için şu kod derlenmez:
static mut SAYAC: u32 = 0;
fn main() {
unsafe {
SAYAC += 1;
println!("{SAYAC}");
}
}
// error: creating a shared reference to mutable static
// note: `#[deny(static_mut_refs)]` (part of `#[deny(rust_2024_compatibility)]`) on by defaultEdition Guide'ın önerdiği yol, mümkünse değiştirilebilir global durumdan kaçınmak, kaçınılamıyorsa iç değişebilirlik sağlayan bir tipi değişmez bir static içinde kullanmaktır. Basit bir sayaç için atomik tipler en kolay seçenektir ve hiç unsafe gerektirmez:
use std::sync::atomic::{AtomicU32, Ordering};
static SAYAC: AtomicU32 = AtomicU32::new(0);
fn main() {
SAYAC.fetch_add(1, Ordering::Relaxed);
SAYAC.fetch_add(1, Ordering::Relaxed);
println!("{}", SAYAC.load(Ordering::Relaxed));
}
// çıktı: 2Atomik tipler ve Ordering seçenekleri Arc ve Mutex ile paylaşılan durum yazısında, unsafe blokların kuralları ise unsafe ve ham işaretçi yazısında anlatılıyor. 2024 edition'ın diğer değişiklikleri için Rust 2024 edition yazısına bakın.
| Özellik | let | const | static |
|---|---|---|---|
| Tip yazmak | Çoğu zaman gerekmez | Zorunlu | Zorunlu |
| Değiştirilebilirlik | mut ile | Asla | static mut ile, unsafe gerekir |
| Değer | Çalışma zamanında hesaplanabilir | Sabit ifade | Sabit ifade |
| Kapsam | Blok içi | Her kapsam, global dahil | Her kapsam, global dahil |
| Bellek adresi | Yığın çerçevesinde | Kullanıldığı yere kopyalanır | Tek ve sabit adres |
| İsim geleneği | snake_case | BUYUK_HARF | BUYUK_HARF |
Sık yapılan hatalar
- mut eklemeyi unutmak:
E0384hatası alırsınız; derleyicininhelp:satırındaki öneriyi uygulayın ya da değişkenin gerçekten değişmesi gerekip gerekmediğini düşünün. - Her yere mut yazmak: Gereksiz
mutuyarı üretir ve okuyucuya yanlış bilgi verir; değişmezlik varsayılan, değişebilirlik bilinçli bir tercih olmalıdır. - mut ile tip değiştirmeye çalışmak:
E0308alırsınız; tip değişecekse shadowing kullanın. - Sabitlerde tip yazmamak:
constvestaticiçin tip belirtmek zorunludur. - Çalışma zamanı değerini sabite atamak: Kullanıcı girdisi ya da dosyadan okunan değer gibi çalışma zamanında belli olan sonuçlar
constolamaz; bunlar içinletkullanın. - static mut ile global sayaç: 2024 edition'da referans alan her kullanım reddedilir; atomik tipleri ya da kilit tabanlı tipleri tercih edin.
Sık Sorulan Sorular
Rust neden değişkenleri varsayılan olarak değişmez yapıyor?
Bir değerin değişmeyeceğini varsayan kodla onu değiştiren kod arasındaki uyumsuzluktan doğan hataları derleme zamanında yakalamak için. Değişebilirliği açıkça mut ile istemeniz, kodu okuyanlara da hangi değerlerin değişeceğini gösterir.
Shadowing performansı etkiler mi?
Shadowing yeni bir değişken tanımlamaktır; belgelerde bunun bir çalışma zamanı maliyeti olduğuna dair bir bilgi yoktur. Asıl farkı anlamsaldır: tip değiştirebilir ve dönüşümden sonra değeri değişmez bırakır.
const mu static mi kullanmalıyım?
Reference'ın önerisine göre genellikle const. Tek bir bellek adresi gerektiğinde, büyük veri saklarken ya da atomik tip gibi iç değişebilirlik sağlayan bir tip kullanırken static seçin.
Alt çizgiyle başlayan değişken adının özel bir anlamı var mı?
Derleyici alt çizgiyle başlayan isimler için kullanılmayan değişken uyarısı vermez. Yalnızca _ yazmak ise değeri hiçbir isme bağlamaz; bu fark desenler konusunda önem kazanır.
let ile tanımlanan değişkenler bellekte nerede durur?
Reference'a göre yerel bir değişken değerini doğrudan yığın (stack) çerçevesinde tutar ve fonksiyona girildiğinde çerçevedeki bütün yerel değişkenler için yer ayrılır; değerler ise başlangıçta atanmamış durumdadır. Bu yüzden derleyici, bir değişkenin her yolda ilk değerini aldığını kanıtlayamazsa onu kullanmanıza izin vermez. String ya da Vec gibi tiplerin asıl içeriği ise heap üzerindedir; bu ayrım ownership yazısında ayrıntılı anlatılıyor.
Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.
İlgili içerikler: Rust öğrenme rehberi · Rust veri tipleri · Ownership · Pattern söz dizimi · Arc ve Mutex · E0277 ve E0308 tip hataları · Rust nedir