? operatörü
Ok ise değeri çıkarır, Err ise fonksiyondan o hatayı hemen döndürür.
İfadenin sonuna yazılan ?, Result (ya da Option) için erken çıkış kısayoludur. Yalnızca Result/Option döndüren fonksiyonlarda kullanılır. Hata tipi farklıysa From ile çevrilir.
fn oku(s: &str) -> Result<i32, std::num::ParseIntError> {
let n = s.parse::<i32>()?;
Ok(n * 2)
}&str
Bir metne ödünç bakış (metin dilimi). Sahibi değildir, değiştirilemez.
&str bir metnin (ya da bir kısmının) üzerine bakan referanstır. Kod içindeki "selam" yazıları &str'dir. Fonksiyon parametresinde &String yerine &str almak daha esnektir.
let a: &str = "selam";
let s = String::from("dünya");
let b: &str = &s;alan
Struct'ın içindeki isimli değer: ogrenci.ad gibi.
Struct tanımındaki her isim: Tip bir alandır. Alanı örnek.alan ile okursun; değiştirmek için örneğin mut olması gerekir. Tek alan mut yapılamaz, tüm örnek yapılır.
let ada = Ogrenci { ad: String::from("Ada"), yas: 12 };
println!("{}", ada.yas);aralık
1..5 (5 hariç) ya da 1..=5 (5 dahil) biçiminde sayı dizisi.
for ile gezilir ya da match içinde kullanılır. .. sonu hariç tutar, ..= sonu dahil eder. .rev() ile tersten gezilir.
for i in (1..=3).rev() {
println!("{i}");
}Arc
`Rc`'nin thread'ler arası kullanılabilen sürümü; sahip sayacı atomiktir.
Arc<T> (atomically reference counted) bir değere birden çok thread'in sahip olmasını sağlar. Arc::clone yalnızca sayacı artırır. İçeriğe tek başına yalnız okuma erişimi verir; değiştirmek için Mutex/RwLock ile birleştirilir.
assert_eq!
Koşul tutmazsa panic eden test makroları: `assert!`, `assert_eq!`, `assert_ne!`.
assert!(koşul) koşulun doğru, assert_eq!(a, b) iki değerin eşit, assert_ne!(a, b) farklı olmasını ister. Tutmazsa panic eder ve test kalır. assert_eq! için tipin hem PartialEq hem Debug uygulaması gerekir. Son argüman, println! gibi biçimlenen bir açıklama olabilir.
assert_eq!(topla(2, 2), 4, "topla(2, 2) sonucu 4 olmalı");
async/await
`async fn` bir Future döndürür; `.await` o Future'ın bitmesini bekler.
async fn çağrılınca gövde çalışmaz, bir Future üretilir. .await yalnız async fonksiyon ya da blok içinde kullanılır; Future Pending ise görevi durdurur ve thread'i başka işe bırakır. Derleyici bunları durum makinesine çevirir.
async fn iki_kat(x: u32) -> u32 { x * 2 }
async fn hesap() -> u32 { iki_kat(3).await }atomik tip
Kilitsiz ve bölünmez işlemlerle thread'ler arasında güvenle paylaşılan sayı/bool tipi.
AtomicUsize::fetch_add(1, Ordering::SeqCst) okuma-artırma-yazmayı tek bölünmez adımda yapar; araya başka thread giremez. Sayaç ve bayrak gibi basit durumlarda Mutex'ten hafiftir. Başlangıçta her yerde Ordering::SeqCst kullan.
bayt
Bellek birimi (8 bit). Rust metinleri UTF-8 baytları olarak saklar.
len() bir metindeki karakter sayısını değil bayt sayısını verir. ASCII harfler 1 bayt, ç ğ ı ö ş ü gibi Türkçe harfler 2 bayttır. Karakter saymak için .chars().count() kullan.
"abc".len(); // 3
"çay".len(); // 4
Big-O
Girdi büyüdükçe işin nasıl arttığını gösteren gösterim: O(1), O(n), O(n²)...
Saniyeyi değil büyüme eğrisini anlatır. O(1) sabit, O(n) girdiyle orantılı, O(n²) karesi kadar (iç içe döngü). 1000 elemanda O(n²) bir milyon adımdır; 100 bin elemanda on milyar. Vec::contains O(n), HashSet::contains ortalama O(1).
birim testi
Tek bir parçayı (fonksiyon, struct) tek başına deneyen, kodun yanındaki test.
#[cfg(test)] mod testler { use super::*; ... } içinde yazılır; alt modül olduğu için üst modülün private öğelerini de görür. Küçük, hızlı ve çok sayıda olur.
blanket impl
Bir trait'i, bir sınırı sağlayan **tüm** tipler için tek `impl` ile uygulamak.
impl<T: Display> Kutula for T yazınca Display uygulayan her tip otomatik Kutula olur. Standart kütüphanenin .to_string() metodu da böyle gelir: Display olan her şeye ToString verilir.
impl<T: std::fmt::Display> Kutula for T {
fn kutula(&self) -> String { format!("[{}]", self) }
}bool
İki değerden birini alan tip: true (doğru) ya da false (yanlış).
Koşullarda kullanılır. Karşılaştırmalar (3 > 2) ve mantık işlemleri (&&, ||, !) bool üretir.
let dogru_mu: bool = true;
borrow checker
Derleyicinin referans kurallarını denetleyen kısmı. Hatalar derleme anında çıkar.
Her referansın ne kadar yaşadığını ve aynı veriye aynı anda kimlerin eriştiğini takip eder. Kural ihlalini program çalışmadan yakalar; böylece veri yarışı ve boşa düşmüş (dangling) referans oluşmaz.
borrowing
Sahipliği almadan bir değeri & ile geçici olarak kullanmak.
°er ile okumak için, &mut deger ile değiştirmek için ödünç alırsın. Kural: aynı anda ya bir tane &mut ya da istediğin kadar & olabilir.
Box
Değeri heap'e koyan, stack'te yalnız adresini tutan tek sahipli akıllı işaretçi.
Box::new(x) değeri heap'e taşır. Boyutu sabit (bir adres) olduğu için kendini içeren tipleri (bağlı liste, ağaç) mümkün kılar. Sahibi kapsamdan çıkınca heap'teki değer silinir. *kutu ile içine ulaşılır.
enum Liste {
Dugum(i32, Box<Liste>),
Son,
}BTreeMap
`HashMap` gibi ama anahtarları sıralı tutar. Gezerken hep sıralı çıktı verir.
Aynı metotlar (insert, get, entry). Fark: döngü her zaman anahtar sırasıyla gider. Sıra Unicode değerine göredir, Türkçe alfabeye göre değil (ç ve ğ, z'den sonra gelir). Sıralı ve tekrarlanabilir çıktı gerektiğinde HashMap'e tercih edilir.
let mut m = BTreeMap::new();
m.insert("muz", 1);
m.insert("armut", 2);
// gezince: armut, muzbuilder deseni
Çok ayarlı nesneyi, zincirlenebilir metotlarla adım adım kurup sonda `olustur()` ile teslim etme kalıbı.
Ayrı bir yapıcı struct'ı ayarları toplar. Her ayar metodu mut self alıp Self döndürür (zincirleme), son metot (build/olustur) self'i tüketip asıl nesneyi verir. Çok parametreli yapıcı fonksiyondan daha okunaklıdır; isteğe bağlı ayarlar için varsayılanlar yapıcıda tutulur.
let i = Istek::yeni("https://kodla.cc").metot("POST").baslik("A", "1").olustur();Cargo.toml
Paketin kimlik kartı: ad, sürüm, edition ve bağımlılıklar. cargo komutları onu okur.
TOML biçiminde yazılan dosya: [package] (name, version, edition) ve [dependencies] bölümleri vardır. cargo new/run/build/check/test komutları projeyi yönetir.
[package]
name = "selam"
version = "0.1.0"
edition = "2024"
cast
as ile bir değeri başka bir sayı tipine çevirmek. Örn. x as f64.
Rust farklı sayı tiplerini kendiliğinden karıştırmaz. x as f64 ile i32'yi f64'e çevirirsin. Küsuratlı bölme için gerekir.
let a: i32 = 7;
let b = a as f64 / 2.0; // 3.5
Cell
`Copy` tipler için referans vermeyen iç değişim hücresi: `get()` kopya alır, `set(v)` yazar.
Cell<T> içeriğine referans vermez; get() kopyasını döndürür (T: Copy gerekir), set(v) üzerine yazar. Çalışma zamanı sayacı ve panic yoktur. Küçük sayaç ve bayraklar için RefCell'den hafiftir.
let c = Cell::new(5);
c.set(c.get() + 1); // 6
char
Tek bir karakter. Tek tırnakla yazılır: 'R'.
char tek bir harf, rakam ya da sembol tutar. 'R' bir char, "R" ise metindir (&str).
let harf: char = 'R';
clone
Heap verisinin de kopyalandığı gerçek kopya. `.clone()` ile yapılır, maliyetlidir.
.clone() heap'te yeni bir yer ayırır ve içeriği oraya kopyalar. Sonuçta iki bağımsız sahip olur, ikisi de kullanılabilir. Veri büyükse yavaştır; bu yüzden gereksiz yere clone yazma.
let a = String::from("x");
let b = a.clone(); // a da b de geçerliclosure
`|x| x + 1` gibi isimsiz küçük fonksiyon; çevresindeki değişkenleri kullanabilir.
Closure, bir değişkende saklayıp fonksiyon gibi çağırabildiğin isimsiz koddur. Parametreler | | arasına yazılır. Normal fn'in aksine çevresindeki değişkenleri yakalayabilir. map, filter, sort_by gibi birçok standart metot closure ister.
let oran = 3;
let uc_kat = |x: i32| x * oran;
println!("{}", uc_kat(5)); // 15copy
Atamada otomatik kopyalanan, taşınmayan basit tipler: i32, bool, char, f64…
Copy tipler yalnızca stack'te yaşar ve bitwise kopyalanması ucuzdur. let b = a; yazdığında a geçerli kalır. Heap'e bağlı tipler (String, Vec) Copy olamaz.
let a = 5;
let b = a; // ikisi de kullanılabilir
Cow
Ödünç (`Borrowed`) ya da sahipli (`Owned`) veri taşıyan enum; kopyayı yalnız gerekince yaparsın.
Cow<str> ya Cow::Borrowed(&str) (kopya yok) ya Cow::Owned(String) (yeni metin) olur. Girdiyi çoğu zaman değiştirmeden döndüren, bazen değiştirmesi gereken fonksiyonlarda gereksiz kopyayı önler. &str gibi okunabilir, into_owned() ile String olur.
fn temizle(s: &str) -> Cow<str> {
if s.contains(' ') { Cow::Owned(s.replace(' ', "_")) } else { Cow::Borrowed(s) }
}crate
Derleyicinin bir seferde derlediği birim: program (main.rs) ya da kütüphane (lib.rs).
Crate iki türlüdür: çalıştırılabilir binary (src/main.rs) ve başkalarının kullandığı library (src/lib.rs). Bir paket (package) bir veya birkaç crate içerir. rand, serde gibi dış paketlere de crate denir. Hiyerarşi: paket > crate > modül.
crates.io
Rust crate'lerinin resmi paket deposu.
cargo bağımlılıkları buradan indirir. Cargo.toml'a ad = "sürüm" yazman ya da cargo add ad demen yeter.
CString
C'nin NUL (`\0`) ile biten metni; `CString` sahipli, `CStr` ödünç alınmış hâlidir.
CString::new(s) metnin sonuna \0 ekler, ortada \0 varsa Err döndürür. c.as_ptr() C'ye verilen *const c_char'dır ve yalnızca c yaşadığı sürece geçerlidir. C'den gelen metin unsafe { CStr::from_ptr(p) } ile ödünç alınır, .to_str() ile &str olur.
let c = CString::new("merhaba").unwrap();
let p = c.as_ptr();dangling reference
Silinmiş bir değeri gösteren referans. Rust bunun oluşmasına izin vermez.
C/C++'ta sahibi silinen verinin adresini tutmak çökmeye ya da güvenlik açığına yol açar. Rust'ta referans her zaman geçerli veriyi gösterir; aksi durum E0597 ile derleme hatasıdır.
// DERLENMEZ: x kapsamdan çıkınca r boşa düşerdi
let r;
{ let x = 5; r = &x; }data race
İki thread'in aynı veriye, en az biri yazarak, eşgüdümsüz erişmesi; tanımsız davranış.
Rust'ın tip sistemi (sahiplik, Send, Sync) data race'i derleme sırasında engeller: kilitsiz paylaşılan değiştirilebilir veri derlenmez.
deadlock
İki thread'in birbirinin tuttuğu kilidi beklemesi yüzünden ikisinin de sonsuza kadar durması.
Thread A önce x'i sonra y'yi, thread B önce y'yi sonra x'i kilitlerse ikisi de takılabilir. Rust bunu derleme sırasında yakalayamaz. Çare: kilitleri her yerde aynı sırayla al, kilidi kısa tut.
debug
{:?} ile değeri geliştirici görünümüyle yazdırmak.
println!("{:?}", "Rust") metni tırnaklarıyla yazar. Liste ve struct gibi tipleri yazdırmak için çok kullanışlıdır.
println!("{:?}", "Rust"); // "Rust"değişken
Bir değere verdiğin isim. let ile tanımlanır.
Bir değeri saklayıp sonra adıyla kullanmanı sağlar. Rust'ta varsayılan olarak değiştirilemez; değiştirmek için let mut kullanılır.
let yas = 7;
let mut sayac = 0;
deref
Bir referansın gösterdiği değere `*` ile ulaşmak. `*x`, `&mut i32` içindeki sayıdır.
x bir referans ise *x onun gösterdiği değerdir. Değeri değiştirmek için *x = ... ya da *x += 1 yazman gerekir. Metot ve alan çağrılarında (o.not, s.len()) Rust referansı kendiliğinden çözdüğü için * yazmazsın.
let mut v = vec![1, 2, 3];
for x in &mut v {
*x *= 2;
}deref zorlaması
Derleyicinin `&Kutu<String>` gibi referansı `&str`'ye kendiliğinden çevirmesi.
Deref uygulayan tiplerde, &T bekleyen yere &Sarmal<T> verirsen Rust deref çağırarak tipi çevirir; gerekirse zincirler (&Box<String> → &String → &str). Metot çağrılarında da iç tipin metotları böyle görünür.
fn selamla(ad: &str) {}
let s = Box::new(String::from("Ada"));
selamla(&s); // &Box<String> -> &String -> &strderive
`#[derive(Debug)]`: derleyiciye bir yeteneği senin için otomatik üretmesini söyler.
Tipin üstüne yazılan #[derive(...)] satırı, derleyiciye Debug gibi yetenekleri otomatik eklettirir. Şimdilik 'sihirli satır' say; nasıl çalıştığını Bölüm 9'da (trait'ler) göreceğiz.
#[derive(Debug)]
struct Nokta { x: i32, y: i32 }derleyici
Rust kodunu çalışabilir programa çeviren araç (rustc). Hataları da o bulur.
Kodunu makine koduna çevirir ve çevirmeden önce tipleri, sahipliği ve ödünç kurallarını kontrol eder. Hata mesajları genellikle sorunu ve çözümü birlikte söyler.
desen
match, if let, let gibi yerlerde değerin şeklini tarif eden yazım: Some(n), (a, b), 1..=9.
Desenler değerlerin şekline uyar ve gerekirse parçalarını değişkenlere bağlar. Sabitler, aralıklar, _, .., |, @, tuple/struct/enum desenleri iç içe kullanılabilir.
match o {
Some(n @ 1..=9) => println!("{n}"),
_ => {}
}destructuring
Struct, tuple ya da enum'u desenle parçalayıp içindekileri değişkenlere almak.
let Nokta { x, y } = p; ya da match kolunda iç içe desenler. Varsayılan olarak değerler taşınır; ref ya da match °er ile ödünç alınır.
let (a, b) = (1, 2);
let Nokta { x, y } = p;dinamik dağıtım
Hangi metodun çağrılacağının çalışma zamanında vtable'dan bulunması; `dyn Trait`'in yolu.
&dyn Trait ile çağrıda gerçek tip bilinmez; metot vtable üzerinden seçilir. Karşılığında farklı tipleri tek listede toplayabilir, derlenen kod miktarını küçük tutarsın.
Display
{} ile yazdırmayı sağlayan, kullanıcıya okunur mesaj üreten yetenek.
{} biçimi Display ister ({:?} ise Debug). Kendi tipin için impl fmt::Display yazar, içinde write!(f, ...) kullanırsın. Display varsa .to_string() de çalışır.
impl fmt::Display for GirisHatasi {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "girdi boş")
}
}doc test
`///` yorumundaki örnek kod bloğu; `cargo test` onu derleyip çalıştırır.
Kod bloğu kütüphaneyi dışarıdan kullanır, yani yalnızca pub öğeleri görür. Bloğun açılış satırına no_run, should_panic, ignore, compile_fail yazılabilir. # ile başlayan satırlar dokümanda gizlenir ama derlenir.
/// ```
/// assert_eq!(benim_kutu::topla(2, 3), 5);
/// ```
pub fn topla(a: i32, b: i32) -> i32 { a + b }döngü
Bir kod bloğunu tekrar tekrar çalıştıran yapı: loop, while, for.
Rust'ta üç döngü vardır: sonsuza kadar tekrar eden loop, koşul doğru kaldıkça tekrar eden while ve bir aralık ya da listeyi gezen for. break döngüden çıkar, continue turu atlar.
for i in 1..=3 {
println!("{i}");
}drop
Sahibi kapsamdan çıkan değerin otomatik silinmesi. Heap belleği geri verilir.
Rust, kapsam biterken sahibi olan her değer için drop çağırır. free yazmana gerek yok ve unutamazsın. Erken silmek için drop(deger) yazabilirsin; bu da değeri taşır. Birden çok değişken varsa ters sırada silinir.
{
let s = String::from("a");
} // s burada drop edilirDSL
Belirli bir iş için tasarlanmış küçük sözdizimi; Rust'ta makrolarla kurulur.
harita!{ "a" => 1 } ya da html!{ .. } gibi yazımlar küçük birer DSL'dir. Makro, bu özel sözdizimini normal Rust koduna çevirir.
durum makinesi
Her an tek bir durumda olan ve olaylarla durumlar arasında geçen sistem.
Rust'ta genelde bir enum'un varyantları durumlar, bir match de geçişlerdir. Geçersiz durum yazılamaz, eksik geçişi derleyici gösterir.
dyn Error
Error yeteneği olan herhangi bir hata tipi; Box<dyn Error> ile kutulanır.
Box<dyn Error>, Error uygulayan her hata tipini tek bir dönüş tipinde toplar. ? hatayı otomatik kutular; "mesaj".into() ile metinden de hata üretilir. Hızlı araçlar ve main için uygundur; hata türüne göre match gerekiyorsa kendi enum'unu seç. dyn ayrıntısı Bölüm 13, Box Bölüm 12'de.
fn calistir() -> Result<(), Box<dyn std::error::Error>> {
let n: i32 = "12".parse()?;
println!("{n}");
Ok(())
}dyn Trait
Tipi derleme sırasında bilinmeyen, ama bir trait'i uyguladığı bilinen değer; `Box<dyn Trait>` ile tutulur.
dyn Sekil "Sekil trait'ini uygulayan herhangi bir tip" demektir. Boyutu bilinmediği için bir işaretçinin arkasında kullanılır: Box<dyn Sekil> ya da &dyn Sekil. Böylece farklı tipleri aynı Vec içinde tutabilirsin; hangi metodun çağrılacağı çalışma zamanında seçilir.
let liste: Vec<Box<dyn Sekil>> = vec![
Box::new(Kare { kenar: 2.0 }),
Box::new(Daire { r: 1.0 }),
];entegrasyon testi
Kütüphaneyi dışarıdan, yalnızca `pub` arayüzle deneyen test (`tests/` klasörü).
Proje kökündeki tests/ klasöründeki her .rs dosyası ayrı bir crate olarak derlenir ve kütüphaneyi use proje_adi::... ile içe alır. Parçaların birlikte çalıştığını doğrular. Yalnızca lib.rs içe alınabilir; main.rs alınamaz.
entry API
`harita.entry(k).or_insert(0)`: anahtar yoksa ekler, her durumda değere `&mut` referans verir.
'Yoksa ekle, varsa güncelle' desenini tek aramayla yapar. or_insert(0) döndürdüğü &mut V üzerinden * ile yazarsın. Sayma, gruplama ve önbellekte tek satırlık kalıp: *sayac.entry(k).or_insert(0) += 1;. Sadece var olanı güncellemek istiyorsan get_mut kullan.
let mut sayac = HashMap::new();
for k in "a b a".split_whitespace() {
*sayac.entry(k).or_insert(0) += 1;
}enum
Sınırlı sayıda seçeneği (varyantı) olan, değeri her an bunlardan yalnızca biri olan tip.
enum ile "bu tipin geçerli değerleri şunlar" dersin. Varyantlar veri taşıyabilir. match tüm varyantları ele almak zorundadır; yeni varyant eklediğinde unutulan yerleri derleyici gösterir.
enum Yon {
Kuzey,
Guney,
}
let y = Yon::Kuzey;eşzamanlılık
Birçok işi dönüşümlü ilerletmek; tek çekirdekte bile olur.
Eşzamanlılıkta işler aynı anda çalışmak zorunda değildir, sırayla ilerletilirler: bir iş beklerken başkası çalışır. Gerçekten aynı anda çalışmaya paralellik denir (birden çok çekirdek ister). Async, eşzamanlılığı az thread ile sağlamanın yoludur.
etiket
Döngüye verilen 'ad etiketi. break 'ad; ile o döngüden çıkılır.
İç içe döngülerde break yalnızca en içtekini bitirir. Dış döngüye 'dis: for ... gibi etiket verip break 'dis; ya da continue 'dis; yazarsan o döngüyü hedef alırsın.
'dis: for a in 1..5 {
for b in 1..5 {
if a * b == 6 { break 'dis; }
}
}executor
Future'ları poll ederek bitiren bileşen. Std'de yoktur; tokio gibi crate'ler ya da kendi yazdığın.
Yürütücü bir Future'ı poll eder; Pending gelirse bekler ve uyandırılınca tekrar poll eder, Ready gelince sonucu alır. Bu bölümde kendi block_on yürütücünü ve görev zamanlayıcını yazıyorsun. Gerçek projelerde tokio kullanılır.
exhaustive
match'in tüm olasılıkları kapsaması zorunluluğu. Eksik kol derleme hatasıdır (E0004).
Derleyici her match'in bütün olası değerleri kapsadığını kanıtlamak zorundadır. Kapsanmayan varyantı hata mesajı söyler. Korumalı (if) kollar hiçbir zaman kapsama sayılmaz.
expect
unwrap gibi: hata varsa panic eder ama kendi mesajını yazar.
.expect("mesaj") başarıda değeri verir, hatada senin mesajınla panic eder. Mesaj, neden hata beklemediğini anlatmalı. Testlerde ve mantıken imkânsız durumlarda uygundur.
let port: u16 = "8080".parse().expect("sabit metin sayı olmalı");FFI
Rust'ın C (ve C arayüzlü) kodlarla fonksiyon çağırarak konuşması.
İki taraf aynı çağrı kuralına (ABI) uymalıdır: extern "C". Edition 2024'te C fonksiyonları unsafe extern "C" { fn ...; } bloğunda bildirilir ve çağrıları unsafe'tir. Rust fonksiyonu C'ye açmak için #[unsafe(no_mangle)] pub extern "C" fn; paylaşılan struct'lar #[repr(C)] ister.
unsafe extern "C" {
fn abs(x: std::ffi::c_int) -> std::ffi::c_int;
}fixture
Testin ihtiyaç duyduğu hazır veri ya da ortam; genelde yardımcı fonksiyonla kurulur.
Her test kendi fixture'ını kurmalıdır, çünkü testler paralel ve belirsiz sırada çalışır. Ortak değiştirilebilir duruma dayanan testler kararsız (flaky) olur.
fn dolu_stok() -> Stok {
let mut s = Stok::yeni();
s.ekle("elma", 5);
s
}Fn
Yakaladığını yalnızca okuyan closure trait'i; istenildiği kadar çağrılabilir.
Fn, FnMut ve FnOnce üçlüsünün en kısıtlı olanıdır. F: Fn(i32) -> i32 sınırı, closure'ın yakaladıklarını değiştirmediğini ve istediğin kadar çağırabileceğini garanti eder. Her Fn closure aynı zamanda FnMut ve FnOnce'tır.
fn iki_kez<F: Fn() -> i32>(f: F) -> i32 {
f() + f()
}FnMut
Yakaladığını değiştirebilen closure trait'i; birden çok kez çağrılır, `mut` ister.
Yakaladığı bir değeri değiştiren closure FnMut'tır. Birçok kez çağrılabilir ama çağıran tarafta mut f: F olarak tutulmalıdır. Her FnMut aynı zamanda FnOnce'tır; Fn isteyen yere verilemez.
fn uc_kez<F: FnMut()>(mut f: F) {
f(); f(); f();
}FnOnce
Yakaladığını tüketebilen closure trait'i; en fazla bir kez çağrılabilir.
Yakaladığı değeri gövdeden dışarı taşıyan (tüketen) closure yalnızca FnOnce'tır; ikinci çağrıda E0382 alırsın. Her closure FnOnce olarak çağrılabildiği için F: FnOnce() en çok closure kabul eden sınırdır.
let ad = String::from("Ada");
let tuket = || ad; // ad'ı dışarı veriyor
let a = tuket(); // bir kezfonksiyon
fn ile tanımlanan, istediğin kadar çağırabildiğin kod parçası.
Bir işi yapan, adı olan kod bloğu. Parametre alabilir ve değer döndürebilir. Son satırda ; yoksa o ifadenin değeri döndürülür.
fn topla(a: i32, b: i32) -> i32 {
a + b
}format!
`println!` gibi biçimlendirir ama ekrana yazmaz; sonucu yeni bir String olarak verir. Hiçbir girdiyi tüketmez.
format!("{a}-{b}") iki metni birleştirir ve bir String döndürür. a + &b'den farkı: a taşınmaz, a ve b sonra da kullanılabilir. Çok parçalı birleştirmelerde en okunaklı yoldur.
let ad = String::from("Ayşe");
let mesaj = format!("Merhaba, {ad}!");From
Bir tipten başka bir tip üretme kuralı. ? hata tiplerini bununla çevirir.
impl From<A> for B, A'dan B yapmanın yolunu tanımlar. ? operatörü hatayı fonksiyonun hata tipine From::from ile çevirir; böylece farklı kaynaklardan gelen hatalar tek enum'da toplanır. From yazarsan .into() de bedavadır.
impl From<ParseIntError> for OkumaHatasi {
fn from(e: ParseIntError) -> Self {
OkumaHatasi::Sayi(e)
}
}Future
İleride bir değer verecek iş; tek metodu `poll`. Tembeldir, poll edilene kadar ilerlemez.
Future trait'inin Output ilişkili tipi ve poll metodu vardır. poll ya Poll::Ready(değer) ya Poll::Pending döndürür. async fn ve async bloklar derleyicinin yazdığı Future'lardır. Future'ı çalıştıran şey yürütücüdür (executor).
trait Future {
type Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>;
}generic
Birden çok tip için çalışan tek bir kod; tip yerine `T` gibi bir yer tutucu yazılır.
Generic kod, tipi bir tip parametresi (T) olarak bırakır; derleyici çağrıya bakıp T'yi doldurur. Aynı fonksiyonu i32, f64, String için ayrı ayrı yazmak yerine bir kez yazarsın. Hız kaybı olmaz; çünkü her tip için derleme sırasında ayrı kopya üretilir.
fn ilk<T>(liste: &[T]) -> &T {
&liste[0]
}getter
Bir alanı okuyan basit metot. Rust'ta adı alanla aynıdır: `bakiye()`.
Alanı doğrudan açmak yerine metotla okutmak, ileride alanı gizleyip (Bölüm 8) kuralları tek yerde tutmanı sağlar. Rust geleneği: get_ öneki yok, hesap.bakiye().
fn bakiye(&self) -> u64 { self.bakiye }gölgeleme
Aynı isimle yeni bir let yazarak eski değişkeni görünmez kılmak.
let x = 5; let x = x + 1; yazınca ikinci x birincisini gölgeler. Yeni bir değişken oluştuğu için tip de değişebilir. mut'tan farkı budur.
let bosluk = " ";
let bosluk = bosluk.len();
ham işaretçi
`*const T` / `*mut T`: ömrü ve geçerlilik garantisi olmayan çıplak adres.
Referanstan farkı: null, sarkan ya da hizasız olabilir, ödünç kuralına tabi değildir. Oluşturmak güvenlidir (&x as *const i32), deref etmek (*p) unsafe ister. p.is_null(), p.add(n), p.read(), p.write(v) temel işlemlerdir.
let x = 5;
let p = &x as *const i32;
let y = unsafe { *p };HashMap
Anahtardan değere hızlı erişim veren koleksiyon. Aynı anahtar bir kez bulunur; sıra garanti edilmez.
HashMap<K, V> anahtar-değer çiftlerini tutar. insert, get (Option döner), contains_key ve remove temel işlemlerdir. Üzerinde dönerken sıra belirsizdir. String anahtar ve değerler insert ile haritaya taşınır. use std::collections::HashMap; gerekir.
let mut puan = HashMap::new();
puan.insert(String::from("ayşe"), 90);
puan.get("ayşe"); // Some(&90)HashSet
Tekrarsız değerler kümesi. Her değer en fazla bir kez bulunur; 'var mı?' sorusu çok hızlıdır.
insert değer yeniyse true, zaten varsa false döner. contains ile üyelik kontrol edilir. Sırası belirsizdir. Tekrarları atmak ve 'daha önce gördüm mü?' kontrolleri için idealdir.
let mut gorulen = HashSet::new();
gorulen.insert("ali"); // true
gorulen.insert("ali"); // falsehata enum'u
Her hata durumuna bir varyant veren, kendi yazdığın hata enum'u.
Hata için String yerine enum kullanmak, hataları match ile güvenle ayırmanı sağlar. Varyantlar veri taşıyabilir. Genelde Debug, PartialEq türetilir ve Display elle yazılır.
#[derive(Debug, PartialEq)]
enum HesapHatasi {
SifiraBolme,
Negatif(i32),
}heap
Büyük ve esnek bellek bölgesi. Boyutu çalışırken değişen veriler burada yaşar.
Yer ayırmak stack'ten yavaştır ama istediğin boyutta veri koyabilirsin. String ve Vec içeriği heap'tedir; stack'te sadece o içeriğe giden bir adres durur. Heap'teki veriyi kimin temizleyeceği sorusunun Rust'taki cevabı sahipliktir.
let s = String::from("merhaba"); // metin heap'te, s stack'tehijyen
Makroda doğan yerel değişkenler, çağıranın aynı adlı değişkenleriyle karışmaz.
Rust macro_rules! makroları hijyeniktir: makro içindeki let x, çağıranın x'ini ezmez, çağırana da görünmez. Bir ismi çağırana açmak için ismi $ad:ident ile çağıran verir. Hijyen yerel değişkenler ve etiketler içindir; makroyla üretilen fn ve struct isimleri normal görünür.
HTTP
Web'in düz metin protokolü: istek satırı, başlıklar, boş satır, gövde.
İstek: GET /yol?a=1 HTTP/1.1, başlıklar, boş satır, isteğe bağlı gövde. Yanıt: HTTP/1.1 200 OK, başlıklar, boş satır, gövde. Satır sonu 'dir. Content-Length gövdenin bayt sayısıdır. Yaygın durum kodları: 200, 201, 204, 400, 404, 405.
GET /selam?ad=Ali HTTP/1.1
Host: 127.0.0.1:7878
Iterator
`next()` ile değerleri tek tek veren trait; `for` döngüsü bununla çalışır.
Iterator trait'i type Item; ve fn next(&mut self) -> Option<Self::Item> içerir. next() her çağrıda Some(değer) verir, bitince None. Uygulayan her tip for ile gezilebilir. Ayrıntılar Bölüm 11'de.
let mut g = Geri { n: 2 };
g.next(); // Some(2)
g.next(); // Some(1)
g.next(); // Noneiç değiştirilebilirlik
Dışarıdan `&` ile görünen bir değerin içini değiştirebilme (`Cell`, `RefCell`).
Normalde &T ile değiştiremezsin. Cell ve RefCell gibi tipler bu kuralı güvenli biçimde esnetir: kuralı derleme yerine çalışma zamanında (ya da kopyalayarak) sağlarlar. Sayaç, önbellek, günlük gibi durumlar için kullanılır.
if let
Tek bir desene uyunca çalışan, match'in kısa yazımı.
if let Some(n) = deger { ... } yalnızca desen uyarsa bloğu çalıştırır. İstersen else ile uymayan durumu yazarsın. Kalıp solda, değer sağdadır.
if let Some(n) = kayit {
println!("{n}");
} else {
println!("yok");
}ifade
Bir değer üreten kod parçası. Örn. 2 + 3 ya da bir if bloğu.
Rust'ta çok şey ifadedir. Sonuna ; koymazsan değeri dışarı verir: fonksiyonun son satırı ya da let x = if ... { } else { }; böyle çalışır.
let x = if 3 > 2 { 10 } else { 20 };ifade ağacı
Token listesinden kurulan, işlem önceliğini gösteren ağaç yapısı (AST).
1 + 2 * 3 düz bir liste değil, kökü + olan bir ağaçtır; * daha derinde durur. Rust'ta özyinelemeli bir enum ile (Box ile) tanımlanır ve değerlendirici ağacı alttan yukarı hesaplar.
enum Ifade {
Sayi(f64),
Ikili(Box<Ifade>, Islem, Box<Ifade>),
}ilişkili fonksiyon
`impl` içinde `self` almayan fonksiyon; `Tip::fonksiyon()` ile çağrılır.
Örneğe değil tipe bağlıdır, o yüzden . değil :: ile çağrılır. Örnek üreten new gibi kurucular bu türdendir (String::from, String::new da öyle).
impl Oyuncu {
fn new(ad: String) -> Self { Self { ad, can: 100 } }
}
let o = Oyuncu::new(String::from("Ada"));ilişkili sabit
Trait ya da `impl` içinde tanımlanan, tipe bağlı sabit: `Tip::SABIT`.
Trait'te const KENAR: u32; yazarsın, her impl kendi değerini verir. Nesne gerekmez: Kare::KENAR ya da generic kodda T::KENAR. Sabit içeren trait dyn olamaz (E0038).
trait Cokgen {
const KENAR: u32;
}
impl Cokgen for Kare {
const KENAR: u32 = 4;
}ilişkili tip
Trait içinde `type Item;` ile bırakılan, uygulayan tipin seçtiği tip.
İlişkili tip, trait metotlarının imzasında Self::Item biçiminde kullanılır; impl bloğunda type Item = u32; ile seçilir. Bir tip bir trait'i yalnızca bir kez uygulayabildiğinde generic parametre yerine tercih edilir. Iterator::Item ve Add::Output örnekleridir.
impl Iterator for Sayac {
type Item = u32;
fn next(&mut self) -> Option<u32> { /* ... */ None }
}impl
Bir tipe metot ve ilişkili fonksiyon ekleyen blok: `impl Tip { ... }`.
impl Tip { ... } içine yazdığın fonksiyonlar Tip'e aittir. Bir tip için birden çok impl bloğu yazabilirsin; hepsi birleşir. Aynı isimde iki fonksiyon olamaz.
impl Trait
Parametrede `x: impl Trait`, dönüşte `-> impl Trait`: gerçek tipi yazmadan yetenek belirtmenin kısa yolu.
Parametrede fn f(x: &impl Tr) ile fn f<T: Tr>(x: &T) aynı işi yapar (her impl parametresi ayrı tip olabilir). Dönüşte çağırana yalnızca trait'in metotlarını gösterir; ama fonksiyon tek bir gerçek tip döndürmek zorundadır.
fn varsayilan() -> impl Ses {
Zil
}irrefutable
Her değere uyan desen: x, (a, b), Nokta { x, y }. let ve fonksiyon parametresi bunu ister.
Uymama ihtimali yoktur. let, fonksiyon parametreleri ve for yalnızca bu tür desenleri kabul eder.
iterator adaptörü
`map`, `filter`, `take` gibi bir iterator'ü yeni bir iterator'e çeviren metot.
Adaptör bir iterator alır ve başka bir iterator döndürür; bu yüzden zincirlenir. Adaptörler tembeldir: tek başlarına hiçbir şey hesaplamazlar, sonuna collect, sum ya da for gibi tüketen bir şey gelince çalışırlar.
let kareler: Vec<i32> = (1..4).map(|x| x * x).collect(); // [1, 4, 9]
join
Başka bir thread'in bitmesini bekleyip dönüş değerini almak.
thread::spawn bir JoinHandle döndürür. .join() thread bitene kadar bekler ve Result verir: thread panic ettiyse Err, yoksa closure'ın döndürdüğü değer Ok içinde gelir.
let t = std::thread::spawn(|| 2 + 3);
let sonuc = t.join().unwrap(); // 5
kanal
Thread'ler arasında mesaj taşıyan, bir gönderici (`Sender`) ve bir alıcıdan (`Receiver`) oluşan boru.
mpsc::channel() multiple producer, single consumer demektir: göndericiyi clone edip çok thread'den yollarsın, alıcı tektir. send değeri kanala taşır; gönderen artık kullanamaz.
kapasite
Bir Vec ya da String'in yeniden yer ayırmadan alabileceği eleman (bayt) sayısı. `len()`'den büyük ya da eşittir.
len() kaç eleman olduğunu, capacity() heap'te kaç elemanlık yer ayrıldığını söyler. Dolu bir Vec'e push yapılırsa Rust daha büyük bir blok ayırır, elemanları oraya kopyalar, eskisini siler. Önceden with_capacity(n) ile yer ayırarak bu maliyeti azaltabilirsin.
let mut s = String::with_capacity(50);
s.push_str("selam");
// len() = 5, capacity() = 50kapsam
Bir değişkenin yaşadığı ve kullanılabildiği kod bölgesi, genelde { } bloğu.
Süslü parantezler bir blok açar. Bloğun içinde tanımlanan değişken blok bitince yok olur. İçerideki aynı isimli let dıştakini geçici olarak gölgeler. Bu fikir Bölüm 3'te sahiplik için de temel olacak.
{
let gecici = 5;
} // gecici burada yok oldukarakter sınırı
Dilim uçlarının bir karakterin tam başına/sonuna denk gelmesi gerekir; yoksa program çöker.
&s[a..b] bayt konumlarıyla çalışır. ç gibi 2 baytlık bir harfin ortasına denk gelen konum geçersizdir ve panic olur. s.is_char_boundary(i) konumun geçerli olup olmadığını söyler. Güvenli yol, chars() ile harf harf ilerlemektir.
let s = "ağaç";
&s[1..3]; // "ğ"
// &s[0..2] -> panic
kooperatif çoklu görev
Görevlerin sırayı kendilerinin bıraktığı model; async'te sırayı `.await` bırakır.
İşletim sistemi thread'leri zorla keser (preemptive); async görevler ise yalnızca .await noktalarında sırayı bırakır. Bu yüzden bir görev içinde thread::sleep ya da uzun CPU döngüsü, aynı thread'deki tüm görevleri dondurur.
koruma
Bir match koluna eklenen ek şart: Some(n) if n > 0.
Desen uyar ve if koşulu doğruysa kol çalışır. Koşul yanlışsa sonraki kola bakılır. Derleyici korumaları kapsama saymaz; korumasız bir kol da gerekir.
match n {
Some(x) if x > 0 => println!("pozitif"),
Some(_) => println!("diğer"),
None => println!("yok"),
}koşul
true ya da false veren ifade. if bu sonuca göre karar verir.
if ile while'ın yanına yazılır ve bool olmalıdır. Parantez gerekmez: if x > 3 { ... }.
if sicaklik > 30 {
println!("Sıcak");
}let else
Desen uymazsa else bloğuyla erken çıkan let. Uyarsa değişkenler aşağıda kullanılır.
let Some(n) = o else { return; }; biçimidir. else bloğu mutlaka çıkmalıdır (return, break, continue, panic!). Böylece iç içe bloklar yerine düz, okunur kod yazarsın.
let Some(ad) = ad else {
return;
};lexer
Ham metni anlamlı parçalara (token) bölen yorumlayıcı aşaması.
Yorumlayıcı hattının ilk adımı: "2 + 3" → [Sayi(2.0), Arti, Sayi(3.0)]. Boşlukları atar, sayıları ve operatörleri tanır, tanımadığı karakterde hata verir. Sonraki aşama parser'dır.
let mut it = "12+3".chars().peekable();
while let Some(&c) = it.peek() { /* ... */ }lifetime
Bir referansın geçerli kaldığı süre; derleyici referansın sahibinden uzun yaşamadığını kanıtlar.
Her referansın bir lifetime'ı vardır: kodun hangi bölümünde kullanılabileceği. Çoğu zaman derleyici bunu kendisi bulur. Birden çok referans içeren imzalarda hangisinin hangisine bağlı olduğunu 'a gibi etiketlerle sen söylersin. Etiket ömrü uzatmaz, yalnızca ilişkiyi anlatır.
fn uzun<'a>(a: &'a str, b: &'a str) -> &'a str {
if a.len() >= b.len() { a } else { b }
}lifetime elision
Derleyicinin üç kurala bakarak lifetime'ı kendisinin doldurması; tutmazsa E0106.
Kural 1: her referans parametre kendi lifetime'ını alır. Kural 2: tek girdi lifetime'ı varsa çıktılara o verilir. Kural 3: &self varsa çıktılar self'inkini alır. Kurallar bir çıktı lifetime'ını belirleyemezse derleyici 'a yazmanı ister (E0106).
fn bas(s: &str) -> &str { &s[..1] }
// derleyici: fn bas<'a>(s: &'a str) -> &'a strlifetime parametresi
`<'a>` ile tanıtılan ömür etiketi; referansları birbirine ya da bir struct'a bağlar.
Tip parametreleri gibi <...> içinde tanıtılır, kesme işaretiyle başlar ('a, 'b). Referansta &'a T biçiminde kullanılır. Birden çok parametre olduğunda lifetime'lar tip parametrelerinden önce yazılır: <'a, T>.
struct Alinti<'a> {
metin: &'a str,
}macro_rules!
Desen eşleştirerek kod üreten makro tanımı: `macro_rules! ad { (desen) => { kod }; }`.
Her kol (desen) => { genişleme }; biçimindedir; kollar match gibi sırayla denenir. Desende $x:expr gibi yakalamalar, genişlemede $x kullanılır. Tanım, çağrıdan önce (yukarıda) olmalıdır.
macro_rules! kare {
($x:expr) => { $x * $x };
}makro
Sonu ! ile biten, derleme sırasında koda dönüşen özel çağrı. Örn. println!
Makrolar derleme sırasında başka koda açılır. Fonksiyondan farkı: değişken sayıda argüman alabilir. En sık göreceğin: println!, vec!, format!.
makro genişletme
Derleyicinin makro çağrısını ürettiği koda çevirmesi; derleme sırasında olur.
Derleyici makro çağrısını bulur, tanımdaki kurala göre kodu üretir, çağrının yerine koyar ve ondan sonra derler. Çalışma zamanında makro diye bir şey kalmaz. Açılımı görmek için cargo expand kullanılır.
marker trait
Metodu olmayan, yalnızca tipe bir özellik damgalayan trait: `Copy`, `Send`, `Sync`, `Sized`.
Marker trait'in gövdesinde fonksiyon yoktur; var olması bile derleyiciye bilgi verir. Send ve Sync gibi bazıları (auto trait) tipin parçalarına bakılarak derleyici tarafından otomatik uygulanır.
match
Bir değeri kalıplarla karşılaştırıp uyan kolu çalıştıran yapı.
Tüm olasılıkları kapsamak zorundadır. Geri kalanları yakalamak için _ kullanılır. Bir değer döndürebilir.
match sayi {
1 => println!("bir"),
2..=9 => println!("küçük"),
_ => println!("büyük"),
}metot
Bir tipin `impl` bloğundaki, ilk parametresi `self` olan fonksiyon: `ornek.metot()`.
Metot, bir tipe bağlı fonksiyondur ve örnek.metot(args) diye çağrılır. İlk parametre &self, &mut self ya da self'tir. self olmayan fonksiyonlara ilişkili fonksiyon denir.
impl Dikdortgen {
fn alan(&self) -> u32 { self.en * self.boy }
}Miri
Rust kodunu yorumlayarak çalıştırıp tanımsız davranışı yakalayan araç (nightly).
Sınır dışı erişim, sarkan işaretçi, ilklenmemiş okuma, hizasızlık, &mut çakışması ve data race'i çalışma anında bulur. Kendi bilgisayarında rustup +nightly component add miri ve cargo +nightly miri test. Bu sitenin editöründe yoktur.
mock
Gerçek bağımlılığın yerine testte kullanılan, aynı trait'i uygulayan sahte tip.
Gerçek e-posta, ağ ya da saat yerine, aynı trait'i uygulayan sahte bir tip takarsın. Mock gelen çağrıları kaydedebilir (RefCell<Vec<..>>) ya da istenen hatayı döndürebilir. mockall gibi crate'ler bunu otomatikleştirir.
modül
Kodu gruplayan isim alanı: mod isim { ... }. Yol: modul::oge.
mod bloğu ilgili fonksiyon, struct ve enum'ları bir isim altında toplar. Farklı modüllerde aynı isimler çakışmaz. Öğeler varsayılan olarak private'tır; pub ile dışarı açılır. Gerçek projelerde her modül ayrı dosyadır (mod gorev; → gorev.rs).
mod turkce {
pub fn selamla() -> String { String::from("Merhaba") }
}
// turkce::selamla()monomorphization
Derleyicinin generic kodu, kullanılan her tip için ayrı somut kopya olarak üretmesi.
ilk::<i32> ve ilk::<String> kullandıysan derleyici iki ayrı fonksiyon üretir. Bu yüzden generic kodun çalışma zamanında ek maliyeti yoktur (sıfır maliyetli soyutlama); bedeli biraz daha büyük program dosyasıdır.
move
Bir değerin sahipliğinin başka bir değişkene geçmesi. Eski değişken artık kullanılamaz.
let b = a; heap'te veri tutan bir tip için (örn. String) sahipliği b'ye taşır. Heap'teki veri kopyalanmaz; yalnızca stack'teki adres kopyalanır ve a geçersiz sayılır. Böylece aynı veri iki kez silinemez. Hata kodu: E0382.
let a = String::from("x");
let b = a; // a artık geçersizmut
let mut ile tanımlanan, sonradan değeri değişebilen değişken.
Rust'ta değişkenler varsayılan olarak değiştirilemez. Değerin sonradan değişecekse let mut yazarsın. mut, mutable (değiştirilebilir) sözcüğünün kısaltmasıdır.
let mut puan = 10;
puan = 20;
mutable referans
&mut ile alınan, değeri değiştirebilen referans. Aynı anda yalnızca bir tane olabilir.
&mut deger ile değeri ödünç alıp değiştirebilirsin. Kural: bir değer için aynı anda ya tek bir &mut ya da istediğin kadar & bulunabilir, ikisi birden olamaz. İhlalde E0499 ya da E0502 hatası alırsın.
let mut s = String::from("a");
let r = &mut s;
r.push('b');Mutex
İçindeki veriye aynı anda yalnız tek thread'in erişmesini sağlayan kilit.
lock() kilidi alır ve bir MutexGuard verir; guard'ın düştüğü anda kilit otomatik açılır. Başka bir thread kilidi tutuyorsa lock() onun bitirmesini bekler. Arc<Mutex<T>> thread'ler arası değiştirilebilir paylaşımın standart kalıbıdır.
let sayac = Arc::new(Mutex::new(0));
*sayac.lock().unwrap() += 1;
newtype
Tek alanlı tuple struct: `struct Metre(f64);`. Aynı tipi farklı anlamlarla ayırır.
Metre(f64) ve Saniye(f64) ikisi de bir f64 taşır ama farklı tiptir. Yanlış birimi yanlış yere vermek derleme hatası olur; çalışma zamanında maliyeti yoktur.
struct Metre(f64);
let uzunluk = Metre(5.0);
println!("{}", uzunluk.0);null
Birçok dilde "değer yok" anlamına gelen özel değer. Rust'ta yoktur; yerine Option vardır.
null kontrolünü unutmak çok yaygın çökme sebebidir. Rust bu olasılığı tipin içine koyar: değer olmayabiliyorsa tip Option<T> olur ve açmadan kullanamazsın.
object safety
Bir trait'in `dyn Trait` olarak kullanılabilmesi için uyması gereken kurallar.
Metodu Self döndüren ya da kendi generic parametresi olan trait'ler dyn olamaz; çünkü gerçek tip bilinmeden vtable kurulamaz. İlgili metoda where Self: Sized yazarsan o metot dyn dışında bırakılır ve trait yine dyn olabilir.
operatör
İşlem sembolü: + - * / ==, <, &&, || gibi.
Karşılaştırma operatörleri (==, !=, <, >, <=, >=) ve mantık operatörleri (&&, ||, !) bool değer üretir.
let ok = yas >= 18 && izin;
operatör aşırı yükleme
Kendi tipin için `+`, `-`, `*` gibi operatörlerin davranışını `std::ops` trait'leriyle tanımlamak.
Rust'ta a + b aslında a.add(b)'dir. impl Add for Tip yazarsan + kendi tipinde çalışır. Sub (-), Mul (*), Div (/), Neg (-a) aynı kalıptadır. add(self, ...) değeri alır; tip Copy değilse işlemden sonra operand kullanılamaz.
impl Add for Vektor {
type Output = Vektor;
fn add(self, d: Vektor) -> Vektor {
Vektor { x: self.x + d.x, y: self.y + d.y }
}
}Option
Değer olabilir de olmayabilir: Some(değer) ya da None. Rust'ın null'u.
Standart kütüphanedeki enum: enum Option<T> { None, Some(T) }. <T> herhangi bir tip için çalışan kalıptır (Bölüm 9'da). Değere match, if let, unwrap_or gibi yollarla ulaşırsın; Result onun hata bilgisi taşıyan kardeşidir (Bölüm 7).
let var: Option<i32> = Some(5);
let yok: Option<i32> = None;
orphan kuralı
Bir trait'i bir tip için ancak trait ya da tip kendi crate'ine aitse uygulayabilirsin.
impl Display for Vec<i32> yazamazsın: ne Display ne Vec senin. Kural, iki crate'in aynı uyumu çakışacak şekilde yazmasını önler. Çözüm: tipi kendi struct'ına sarmak (newtype) ve trait'i onun için yazmak.
ownership
Her değerin tek bir sahibi vardır; sahip kapsamdan çıkınca değer silinir.
Rust'ın bellek yönetimi kuralı. Her değerin tek bir sahibi olur. Sahip kapsam dışına çıkınca değer otomatik silinir (drop). Atama ya da fonksiyona verme sahipliği taşır (move).
örnek
Struct şablonundan üretilmiş, alanlarına gerçek değer verilmiş tek bir değer.
Ogrenci { ad: ..., yas: ... } yazınca Ogrenci tipinin bir örneği oluşur. Tüm alanlar verilmek zorundadır. Aynı tipten istediğin kadar örnek üretebilirsin.
özellik tabanlı test
Tek örnek yerine 'her girdi için doğru olan kural'ı rastgele girdilerle sınayan test.
Örnek: ters(ters(v)) == v ya da 'sıralama çıktısı sıralıdır ve aynı uzunluktadır'. Test, çok sayıda rastgele girdi üretir; hata bulursa girdiyi küçültüp (shrinking) bildirir. Rust'ta proptest ve quickcheck crate'leri kullanılır.
özyinelemeli iniş
Gramerin her kuralının bir fonksiyon olduğu, özyinelemeli çalışan parser tekniği.
ifade → terim → carpan gibi çağrı zinciri işlem önceliğini belirler; düşük öncelikli kural üstte, yüksek öncelikli kural derinde olur. Parantez, carpan içinden yeniden ifade'yi çağırır.
özyinelemeli makro
Genişlemesinde kendini daha küçük girdiyle çağıran makro; taban kural şart.
Tipik kalıp: bir taban kol (tek eleman ya da boş girdi) ve ilk elemanı ayırıp geri kalanıyla kendini çağıran kol. Taban kural yoksa derleyici 128 derinlikte recursion limit reached hatası verir.
panic
Programı bir hata mesajıyla anında durduran mekanizma: panic!("mesaj").
panic! programcı hatasını yüksek sesle bildirmek için kullanılır (geçersiz bir durum oluştu, devam edemeyiz). Kullanıcı girdisi gibi beklenen hatalar için Result kullanılır (Bölüm 7).
if yuzde > 100 {
panic!("yüzde 100'ü aşamaz");
}paralellik
İşleri birden çok çekirdekte gerçekten aynı anda çalıştırmak.
Eşzamanlılıktan farkı: iki iş aynı anda çalışır. Thread'lerle (Bölüm 14) elde edilir. Bir iş ağırlıklı olarak CPU hesabıysa paralellik, ağırlıklı olarak beklemeyse eşzamanlılık (async) uygundur.
parametre
Fonksiyonun dışarıdan aldığı değer. Adı ve tipi yazılır.
fn kare(x: i32) içindeki x bir parametredir. Fonksiyonu çağırırken verdiğin değere (kare(4)) argüman denir.
fn kare(x: i32) -> i32 {
x * x
}parça belirleyici
`$x:expr`'deki `expr`: makroya verilen kodun hangi türde parça olduğunu söyler.
En sık kullanılanlar: expr (ifade), ident (isim), ty (tip), literal (sabit değer), pat (desen), tt (tek token ağacı). Önce en dar olanı seç.
parse
Metni sayıya çevirir. Örn. "42".parse::<i32>().
Girdiden gelen metin sayı değildir. parse() onu istediğin tipe çevirmeye çalışır. Tip için ya let n: i32 = yaz ya da parse::<i32>().
let n = "42".parse::<i32>().unwrap();
PhantomData
Hiç yer kaplamayan, yalnızca bir tip parametresini "kullanılıyor" göstermek için konan alan.
Struct'ta tip parametresi Birim hiçbir alanda geçmiyorsa derleyici hata verir (E0392). PhantomData<Birim> alanı eklersen parametre kullanılmış sayılır; hafızada 0 bayt yer kaplar. Olcu<Metre> ile Olcu<Santim> böylece ayrı tip olur.
use std::marker::PhantomData;
struct Olcu<Birim> {
deger: f64,
_birim: PhantomData<Birim>,
}Pin
Bir değerin bir daha taşınmayacağını garanti eden sarmalayıcı; `poll` bunu ister.
async durum makineleri kendi alanlarına işaret edebilir; taşınırlarsa işaretçi bozulur. Pin<P> sabitlenen değerin yerinden oynamayacağını garanti eder. Yığında pin!(f), heap'te Box::pin(f) ile sabitlenir.
Poll
`poll`'un cevabı: `Ready(değer)` bitti, `Pending` henüz değil.
enum Poll<T> { Ready(T), Pending }. Pending dönen Future, ileride uyandırılacağını garanti etmelidir (Waker). Ready döndükten sonra tekrar poll edilmemelidir.
procedural makro
Token'ları alıp token üreten Rust fonksiyonu; ayrı `proc-macro` crate'inde yaşar.
Üç türü var: derive (#[derive(X)]), attribute (#[x]) ve function-like (x!(..)). syn ile girdi ayrıştırılır, quote! ile kod üretilir. macro_rules!'dan güçlüdür ama ayrı crate gerektirir ve derlemeyi yavaşlatır.
pub use
Öğeyi içe getirip aynı anda dışarıya açan use: iç yapıyı gizler.
pub use self::dahili::islem; ile iç (private) modüldeki bir öğe, bulunduğun modülün kendi üyesiymiş gibi dışarıya açılır. İç düzen değişse bile dış yol sabit kalır; standart kütüphane de böyle çalışır.
mod kutuphane {
mod dahili { pub fn islem() -> i32 { 42 } }
pub use self::dahili::islem;
}Rc
Sahipleri sayan akıllı işaretçi: bir değerin birden çok sahibi olur, sayaç 0'da değer silinir.
Rc::clone(&a) değeri kopyalamaz, sahip sayacını 1 artırır. İçerik salt okunurdur (değiştirmek için RefCell). Yalnızca tek thread içindir; thread'ler arası kardeşi Arc'tır. Rc::strong_count(&a) sahip sayısını verir.
let a = Rc::new(String::from("ortak"));
let b = Rc::clone(&a);
println!("{}", Rc::strong_count(&a)); // 2RefCell
Ödünç kurallarını çalışma zamanında denetleyen hücre; `&self` ile içeriği değiştirmene izin verir.
borrow() okuma, borrow_mut() yazma izni verir. Kurallar derleyici yerine program çalışırken denetlenir; ihlalde (aynı anda iki yazıcı gibi) panic olur. Rc<RefCell<T>> çok sahipli ve değiştirilebilir veri demektir.
let c = RefCell::new(5);
*c.borrow_mut() += 1;
println!("{}", c.borrow()); // 6referans
& ile oluşturulan, sahipliği almadan bir değere bakan işaretçi.
°er bir paylaşımlı referanstır: değeri okuyabilirsin, silinmesinden ya da taşınmasından sorumlu değilsin. Referans, gösterdiği değerden uzun yaşayamaz. Değiştirmek için &mut deger gerekir.
let s = String::from("kitap");
let r = &s;
println!("{r}");referans döngüsü
İki `Rc`'nin birbirini tutması: sayaçlar 0'a inmez, bellek sızar.
a b'yi, b de a'yı Rc ile tutarsa ikisinin güçlü sayacı hep en az 1 kalır ve ikisi de silinmez. Rust bunu derleme sırasında engellemez (sızıntı güvenli sayılır). Çözüm: döngüdeki bir yönü Weak yapmak.
refutable
Bazı değerlere uymayabilen desen: Some(x), 1..=5. let ile tek başına kullanılamaz.
match kolları, if let, while let ve let else reddedilebilir desen kabul eder. Düz let kabul etmez (E0005).
REST
Kaynakları URL ile, işlemleri HTTP metodlarıyla (GET/POST/PUT/DELETE) anlatan API tarzı.
CRUD: Create (POST, 201), Read (GET, 200), Update (PUT, 200), Delete (DELETE, 204). Hata durumlarında 400 (hatalı istek) ve 404 (kaynak yok) kullanılır. Yanıt gövdesi genellikle JSON'dur.
Result
Başarı (Ok) ya da hata (Err) taşıyan enum: Result<T, E>.
Result<T, E> iki varyantlı bir enum'dur: Ok(T) başarıyı, Err(E) hatayı taşır. Başarısız olabilen işlemler (parse, dosya okuma) bunu döndürür. İçindeki değeri kullanmadan önce match, ? ya da unwrap_or ile açman gerekir.
fn bol(a: i32, b: i32) -> Result<i32, String> {
if b == 0 { Err(String::from("sıfıra bölünemez")) } else { Ok(a / b) }
}RwLock
Aynı anda çok okuyucuya ya da tek yazıcıya izin veren kilit.
read() paylaşımlı okuma, write() özel yazma kilidi verir. Okuma çok, yazma az olan verilerde Mutex'ten verimlidir; yazıcı varken herkes bekler.
sabit
const ile tanımlanan, hiç değişmeyen ve tipi zorunlu olan değer.
const MAX_PUAN: u32 = 100; biçiminde yazılır. İsim BÜYÜK_HARF olur, tip zorunludur, mut olamaz. main'in dışında da tanımlanabilir.
const DAKIKA_SAAT: u32 = 60;
scoped thread
`thread::scope` içinde başlatılan, kapsam bitmeden otomatik beklenen ve yerel değişkenleri ödünç alabilen thread.
Normal spawn closure'ının 'static olmasını ister. thread::scope bloğu bitince tüm thread'lerin bittiği garantidir; bu yüzden içindeki thread'ler çevredeki değişkenleri move olmadan referansla kullanabilir.
select!
Birkaç Future'dan ilk bitenle devam eder, diğerlerini iptal eder (`join!` ise hepsini bekler).
tokio::select! Future'ları birlikte poll eder; biri bitince onun koluna girer ve kalanları düşürür (drop = iptal). Zaman aşımı yazmanın klasik yoludur. tokio::join! ise hepsinin bitmesini bekler.
self
Metodun üzerinde çalıştığı örnek. `Self` ise impl'in tipinin kısa adı.
Metotta ilk parametre: &self okur, &mut self değiştirir, self örneği tüketir (taşır). Büyük harfli Self ise impl yazılan tipin kısa adıdır: -> Self = -> Dikdortgen.
fn alan(&self) -> u32 { self.en * self.boy }
fn new(en: u32, boy: u32) -> Self { Self { en, boy } }semantik sürümleme
Sürüm numarası MAJOR.MINOR.PATCH: MAJOR değişirse uyum bozulabilir.
PATCH: hata düzeltme. MINOR: uyumlu yeni özellik. MAJOR: uyumu bozan değişiklik. Cargo'da "1.4" = >=1.4.0, <2.0.0; 0.x sürümlerde MINOR 'büyük' sayılır ("0.8" = >=0.8.0, <0.9.0).
Send
Tipin sahipliğinin başka bir thread'e taşınabildiğini gösteren marker trait.
thread::spawn closure'ın içindeki her şeyin Send olmasını ister. i32, String, Vec<T> Send'dir; Rc<T> değildir (sayacı atomik değil), bu yüzden thread'e geçemez.
should_panic
Testin panic etmesini bekleyen öznitelik; panic ederse test geçer.
#[should_panic] testin mantığını tersine çevirir: fonksiyon panic ederse test geçer, etmezse kalır. #[should_panic(expected = "metin")] panic mesajının o metni içermesini de ister. Result döndüren testlerle birlikte kullanılamaz.
#[test]
#[should_panic(expected = "sıfıra bölme")]
fn bolen_sifir() { bol(1, 0); }sıfır maliyetli soyutlama
Soyutlamayı kullanmanın, elle yazılmış eşdeğer koda göre ek çalışma maliyeti getirmemesi.
Rust'ın tasarım ilkesi: iterator zincirleri, generic'ler ve trait'ler derleme sırasında (inline ve monomorphization ile) elle yazılmış döngü/koda dönüşür. Kullanmadığın şeyin bedelini ödemezsin; kullandığını elle yazsan daha iyisini yazamazsın. Gerçek maliyet gereksiz collect() ve clone() gibi bellek ayırmalarda olur.
Sized
Boyutu derleme sırasında bilinen tip; generic parametreler varsayılan olarak `Sized` ister.
i32, String, Vec<T> Sized'dır. str, [T] ve dyn Trait değildir (boyutları değişir), yalnızca işaretçi arkasında yaşarlar. Generic T'nin bunları da kabul etmesini istersen T: ?Sized yazarsın.
slice
Bir dizinin ya da metnin ardışık bir parçasına referans: &s[0..3].
Slice sahip değildir; konum ve uzunluktan oluşur ve asıl veriyi ödünç alır. &s[1..4] indeks 1'den 4'e kadar (4 hariç) parçayı verir. Metinlerde sınırlar bayt cinsindendir ve karakter sınırına denk gelmelidir.
let s = String::from("merhaba");
let ilk = &s[0..3]; // "mer"
let son = &s[4..]; // "aba"soundness
Güvenli kodla çağrılınca asla UB'ye yol açmayan API'nin özelliği.
İçinde unsafe olan bir fonksiyon, güvenli kodla hiçbir girdi/çağrı sırasında UB üretemiyorsa sound'dur. Bir yolu bile varsa unsound'dur ve hatadır: ya girdiyi denetle ya da fonksiyonu unsafe fn yap.
stack
Hızlı, düzenli bellek bölgesi. Boyutu derleme anında belli olan değerler burada durur.
Üst üste dizilmiş tabaklar gibi çalışır: son konan ilk çıkar. i32, bool, char, f64 gibi sabit boyutlu değerler ve String'in küçük 'künye' kısmı (adres, uzunluk, kapasite) stack'te yaşar. Fonksiyon bitince o fonksiyonun stack alanı kendiliğinden temizlenir.
let x = 5; // stack
let aktif = true; // stack
static
Programın tüm çalışma süresi boyunca geçerli lifetime; string literal'ler `&'static str`'dir.
"merhaba" gibi literal metinler program dosyasına gömülür ve hiç silinmez; tipleri &'static str'dir. const ve static sabitleri de böyledir. 'static yazmak yerel bir değerin ömrünü uzatmaz; derleyici hatasını susturmak için kullanma.
fn selam() -> &'static str {
"merhaba"
}statik dağıtım
Hangi metodun çağrılacağının derleme sırasında seçilmesi; generic'lerin yolu.
fn f<T: Trait>(x: &T) için derleyici her kullanılan tipe ayrı bir kopya üretir (monomorphization), çağrı doğrudan yapılır. Hızlıdır ve satır içine açılabilir; ama bir Vec içinde farklı tipleri tutamaz.
stdin
Programa dışarıdan gelen yazı. Klavyeden ya da kontrol sisteminden okunur.
std::io::stdin().read_line(&mut metin) bir satır okuyup metin'in içine yazar. Satır sonu karakteri de gelir, bu yüzden trim() kullanılır.
let mut satir = String::new();
std::io::stdin().read_line(&mut satir).unwrap();
Stream
Async iterator: öğeler zamanla gelir, `next().await` ile alınır.
Iterator'ın async karşılığıdır. while let Some(x) = akis.next().await ile tüketilir; None akış bitti demektir. Std'de henüz kararlı değil; futures ve tokio-stream crate'leri sağlar.
string
Heap'te yaşayan, büyüyebilen, sahibi olan metin tipi. `String::from("...")` ile oluşur.
String metnin sahibidir: heap'te yer ayırır, büyütebilirsin (push_str) ve sahibi kapsamdan çıkınca silinir. Stack'te yalnızca adres, uzunluk ve kapasite (24 bayt) tutulur.
let mut s = String::from("selam");
s.push_str(" dünya");struct
Birkaç değeri isimli alanlarda toplayan, kendi tanımladığın veri tipi.
struct, farklı tipteki değerleri tek bir tipte birleştirir. Her değerin bir alan adı vardır. Tanımı bir şablondur; gerçek değer üretmek için bir örnek oluşturursun.
struct Ogrenci {
ad: String,
yas: u8,
}struct update
`Tip { alan: x, ..diger }`: verilmeyen alanları başka bir örnekten almak.
..diger yazımı kalan alanları diger örneğinden alır ve en sonda durur. Dikkat: String gibi Copy olmayan alanlar diger'den taşınır, diger kısmen kullanılamaz hale gelir.
let b = Oyuncu { can: 50, ..a };supertrait
Bir trait'in şart koştuğu başka trait: `trait A: B` yazıldığında `A`'yı uygulayan her tip `B`'yi de uygulamalıdır.
trait Hayvan: Display { ... } ile "Hayvan olan her tip yazdırılabilir de olmalı" dersin. Varsayılan metotların içinde {} ile yazdırma gibi Display özelliklerini rahatça kullanırsın.
trait Hayvan: std::fmt::Display {
fn tanit(&self) -> String { format!("Ben {}", self) }
}Sync
`&T`'nin birçok thread tarafından aynı anda paylaşılabildiğini gösteren marker trait.
T: Sync ise &T güvenle başka thread'e verilebilir. Mutex<T> ve atomik tipler Sync'tir; Cell ve RefCell değildir (kontrolleri thread güvenli değil).
tanımsız davranış
`unsafe` sözleşmesi bozulunca ortaya çıkan, sonucu belirsiz davranış; derleyici onun olmadığını varsayar.
Null/sarkan deref, sınır dışı erişim, data race, geçersiz değer (bool = 2), ilklenmemiş okuma, hizasız adres ve çakışan &mut UB'dir. UB olan program bazen doğru çalışır, bazen çöker; test geçmesi UB olmadığını kanıtlamaz. Miri gibi araçlar yakalar.
taşma
Sayının tipin sığdırabileceği aralığı aşması. Örn. u8'de 255 + 1.
Debug derlemede taşma panic ile programı durdurur; release derlemede sessizce başa sarar. Bilinçli davranış için checked_*, wrapping_* ve saturating_* metotları vardır.
let a: u8 = 250;
println!("{}", a.saturating_add(10)); // 255TDD
Önce başarısız test yaz (kırmızı), geçir (yeşil), sonra temizle (refactor).
Her döngüde küçük bir davranış eklenir. Yeşil adımda testi geçiren en küçük kod yazılır; genelleştirmeyi yeni testler zorlar. Refactor sırasında davranış değişmez, yeşil testler bunun güvencesidir.
tekrar deseni
`$( ... ),*` ile değişken sayıda öğe yakalamak; genişlemede `$( ... )*` her biri için yazılır.
* sıfır ya da daha çok, + bir ya da daha çok, ? sıfır ya da bir. Ayraç (örneğin ,) tekrarlar arasına konur. Sondaki virgülü kabul etmek için $(,)? eklenir. Tekrarlar iç içe de olabilir.
( $( $x:expr ),* ) => {{
let mut v = Vec::new();
$( v.push($x); )*
v
}};tembel değerlendirme
Hesabın ihtiyaç duyulana kadar ertelenmesi; iterator zincirleri bu şekilde çalışır.
map ve filter yazmak hiçbir elemanı işlemez, yalnızca bir plan kurar. Plan, tüketen bir metot çağrılınca eleman eleman çalışır. Bu sayede sonsuz kaynaklardan (1..) sonlu sonuç alınır ve gereksiz ara listeler kurulmaz.
let ilk_uc: Vec<i32> = (1..).map(|x| x * 10).take(3).collect(); // [10, 20, 30]
thread
Programın içinde, diğerleriyle aynı anda çalışabilen bağımsız bir yürütme hattı.
std::thread::spawn yeni bir thread başlatır. Thread'ler aynı belleği paylaşır ama hangisinin ne zaman çalışacağı belirsizdir; bu yüzden çıktıyı sıraya bağlı yazmak yerine sonuçları join ile toplarız.
thread havuzu
Önceden açılmış sabit sayıda işçi thread'in ortak bir iş kuyruğundan iş aldığı yapı.
Her iş için thread açmaktan ucuz ve sınırlıdır. Rust'ta kanal (mpsc) ile işler gönderilir, alıcı Arc<Mutex<Receiver<..>>> ile işçiler arasında paylaşılır. Drop içinde gönderici bırakılıp işçiler join edilir.
tip
Değerin ne tür bir şey olduğu: i32, f64, bool, char, &str…
Rust her değerin tipini derleme sırasında bilir. Böylece metni sayıyla toplamak gibi hatalar program çalışmadan yakalanır.
tip çıkarımı
Derleyicinin tipi senin yerine koddan bulması. `vec![1, 2]` için `Vec<i32>`.
Her yerde tip yazmak zorunda değilsin. let mut v = Vec::new(); v.push(1); yazdığında derleyici ilk push'tan listenin Vec<i32> olduğunu çıkarır. Sonra farklı tipte bir değer eklersen E0308 mismatched types hatası alırsın.
tip parametresi
`<T>` ile tanıtılan, gerçek tipin yerine geçen isim.
struct Kutu<T> ya da fn ilk<T>(...) içindeki T bir tip parametresidir. Aynı harf aynı tipi gösterir; farklı tipler için <A, B> gibi birden çok parametre açılır.
struct Cift<A, B> {
ilk: A,
ikinci: B,
}tokio
Rust'ın en yaygın async yürütücüsü ve I/O kütüphanesi (crate).
Çok thread'li yürütücü, zamanlayıcı, ağ ve dosya I/O'su, kanallar ve kilitler sağlar. #[tokio::main] ile async fn main yazılır, tokio::spawn ile görevler başlatılır. Dış crate olduğu için bu sitedeki sandbox'ta çalışmaz.
trait
Bir tipin sağlaması gereken metotlar kümesi; davranış sözleşmesi.
trait metot imzalarını (ve isteğe bağlı varsayılan gövdelerini) tanımlar. impl Trait for Tip ile bir tip sözleşmeyi yerine getirir. Başka dillerdeki interface kavramına benzer.
trait Hayvan {
fn ses(&self) -> String;
}
struct Kedi;
impl Hayvan for Kedi {
fn ses(&self) -> String { String::from("miyav") }
}trait bound
Generic tipe konan yetenek şartı: `T: Hayvan` yalnız `Hayvan` olan tipleri kabul eder.
Sınırsız T ile hiçbir işlem yapılamaz; derleyici T'nin ne yapabildiğini bilmez. T: Trait ile yeteneği garanti edersin. Birden çok şart + ile, uzun şartlar where ile yazılır. Sağlamayan tip verilirse E0277 hatası alırsın.
fn buyuk<T: PartialOrd>(a: T, b: T) -> T {
if a > b { a } else { b }
}tuple
Birkaç değeri parantez içinde tek değer yapan yapı: (3, true).
Farklı tipte değerleri bir arada tutar: (3, true, 'a'). match'e tuple verip birden fazla koşulu aynı anda eşleyebilirsin.
match (x % 3 == 0, x % 5 == 0) {
(true, true) => println!("ikisi de"),
_ => println!("başka"),
}tüketen adaptör
Zinciri çalıştırıp tek bir sonuç veren metot: `collect`, `sum`, `count`, `fold`, `find`...
Tüketen metotlar iterator'ü baştan sona (ya da cevabı bulana dek) yürütür ve bir değer üretir. Zincirde onlar olmadan adaptörler çalışmaz. Tüketilen iterator bir daha kullanılamaz.
let toplam: i32 = vec![1, 2, 3].iter().sum(); // 6
unit struct
Hiç alanı olmayan struct: `struct Isaret;`.
Veri taşımaz, sadece bir tip adıdır. Özellikle trait'lerle (Bölüm 9) davranış eklemek için kullanılır.
struct Isaret;
let _i = Isaret;
Unpin
Taşınmasında sakınca olmayan tipler; Pin onlara kısıt getirmez. Çoğu tip otomatik Unpin'dir.
Kendine işaret etmeyen tipler otomatik Unpin olur (Send gibi işaretçi bir trait). Unpin tipler için Pin::new(&mut x) çalışır ve alanlara doğrudan erişilir. async blok ve fonksiyonlar Unpin değildir.
unsafe
Derleyicinin kanıtlayamadığı güvenliği programcının üstlendiği blok/fonksiyon işareti.
unsafe yalnızca 5 yetenek açar: ham işaretçiyi deref etmek, unsafe fn çağırmak, static mut'a erişmek, unsafe trait uygulamak, union alanı okumak. Sahiplik ve ödünç kuralları açık kalır. İyi pratik: unsafe'i küçük tut, nedenini // SAFETY: yorumuyla yaz ve dışarıya güvenli bir API sun.
let v = vec![10, 20, 30];
// SAFETY: 1 < v.len()
let x = unsafe { *v.get_unchecked(1) };unwrap
Başarılı sonucu çıkarır; hata varsa programı çökertir (panic).
parse() ve read_line() başarısız olabilir. .unwrap() başarı durumunda değeri verir, hata durumunda programı durdurur. Hataları düzgün yönetmeyi Bölüm 7'de öğreneceğiz.
let n: i32 = "42".parse().unwrap();
use
Uzun bir yolu kısaltan içe aktarma: use crate::ev::mutfak;
use yol::oge; öğeyi bulunduğun modülde kısa adıyla kullanılabilir yapar. as ile yeniden adlandırılır, {a, b} ile birden çok öğe alınır. use yalnızca yazıldığı modülde geçerlidir; alt modül devralmaz.
use std::collections::{HashMap, HashSet};
use crate::ev::mutfak::yemek as hazirla;usize
İşaretsiz tam sayı tipi. Uzunluk ve indeksler için kullanılır; negatif olamaz.
Bilgisayarın adres genişliği kadar büyüktür (64 bit makinede 64 bit). len(), count() ve indeksler usize verir.
let n: usize = "merhaba".len();
UTF-8
Rust'ın metin kodlaması: her karakter 1–4 bayt yer kaplar.
Rust metinleri UTF-8'dir. Bu sayede Türkçe harfler ve emojiler sorunsuz saklanır; ama s[0..1] gibi bir dilim karakterin ortasına denk gelirse program panic eder.
varyant
Bir enum'un seçeneklerinden her biri: Yon::Kuzey, Option::Some.
Varyantlar enum'un adı altında yazılır (Yon::Kuzey). Veri taşımayabilir (Cik), tuple gibi (Yaz(String)) ya da struct gibi (Tasi { x, y }) veri taşıyabilir.
enum Mesaj {
Cik,
Yaz(String),
Tasi { x: i32, y: i32 },
}Vec
Heap'te yaşayan, büyüyebilen liste. Tüm elemanlar aynı tiptedir: Vec<i32>, Vec<String>.
Vec<T> elemanları bellekte yan yana tutar; push ile sona eklersin, v[i] ya da get(i) ile okursun. <T> bir yer tutucudur: içindeki elemanın tipi (generic'leri Bölüm 9'da göreceğiz). Kapasite dolunca daha büyük bir heap bloğu ayrılır ve elemanlar oraya taşınır.
let mut v = vec![1, 2, 3];
v.push(4);
println!("{}", v.len()); // 4VecDeque
İki ucundan hızlı ekleme/çıkarma yapılan liste. Kuyruk (queue) için birebir.
push_back/pop_front ile kuyruk, push_front/pop_back ile diğer uçtan çalışır. Vec'in başından silmek (remove(0)) her elemanı kaydırdığı için yavaştır; baştan çıkarma gerekiyorsa VecDeque kullan.
let mut k = VecDeque::new();
k.push_back(1);
k.push_back(2);
k.pop_front(); // Some(1)
vtable
Trait nesnesinin hangi metotları çağıracağını gösteren, tip başına bir tane olan fonksiyon adresi tablosu.
&dyn Trait aslında iki işaretçidir (kalın işaretçi): biri verinin kendisine, biri o tipin vtable'ına bakar. Metot çağrılınca derleyici vtable'dan doğru fonksiyonu bulup çağırır. Bu küçük bir dolaylılık maliyetidir; derleme sırasında seçilen generic çağrıdan biraz yavaştır.
Waker
Yürütücüye "bu görevi tekrar poll et" demenin yolu; `Context` içinde gelir.
Future Pending dönmeden önce cx.waker() ile Waker'ı kullanmalıdır: ya hemen wake_by_ref() çağırır ya da Waker'ı saklayıp olay gerçekleşince wake() der. Aksi halde yürütücü uyumaya geçer ve hiç uyanmaz.
cx.waker().wake_by_ref(); // beni hemen tekrar poll et
Poll::Pending
Weak
`Rc`'ye bağlı ama sahip sayılmayan işaretçi; `upgrade()` ile `Option<Rc<T>>` verir.
Rc::downgrade(&a) bir Weak üretir; güçlü sayaç artmaz, değer yalnız güçlü sahipler bitince silinir. weak.upgrade() değer hâlâ yaşıyorsa Some(Rc), silinmişse None döndürür. Ağaçlarda çocuktan ebeveyne bağlantı ve referans döngülerini kırmak için kullanılır.
let g = Rc::new(5);
let z = Rc::downgrade(&g);
println!("{:?}", z.upgrade()); // Some(5)while let
Desen uyduğu sürece dönen döngü. Örn. pop None dönene kadar.
while let Some(x) = yigin.pop() { ... } her turda deseni dener; uymazsa döngü biter.
let mut yigin = vec![1, 2, 3];
while let Some(x) = yigin.pop() {
println!("{x}");
}workspace
Birden çok paketi ortak Cargo.lock ve target/ ile yöneten çatı proje.
Kökteki Cargo.toml içine [workspace] members = ["a", "b"] yazılır. Üyeler bağımlılıkları bir kez derler. Yerel paketler path = "../a" ile birbirine bağlanır.
[workspace]
members = ["cekirdek", "arac"]
resolver = "3"
yakalama
Closure'ın dış değişkenleri `&`, `&mut` ya da sahiplik olarak içine alması.
Closure gövdesinde kullandığı dış değişkenleri yakalar. Derleyici en hafif yolu seçer: yalnız okuyorsa paylaşımlı ödünç (&), değiştiriyorsa &mut, değeri tüketiyorsa sahiplik. move yazarak sahipliği zorlayabilirsin; closure'ı fonksiyondan döndürürken ya da thread'e verirken gerekir.
let mut sayac = 0;
let mut arttir = || sayac += 1; // sayac'ı &mut yakaladı
arttir();
yorum
// ile başlayan, Rust'ın yok saydığı not satırı.
Kodu okuyana (sana!) açıklama bırakmak için kullanılır. Program çalışırken hiçbir etkisi yoktur.
// Bu satır sadece bir nottur