Ders 01 / 12
Rust'ın Tasarım Hedefleri
On sekiz yazımlık bir ölçüm çekirdeği program içinden koşturulur ve her yazımı üç sınıftan birine yerleştirir — ifade, deyim, öğe; sonuç sekizi ifade, beşi deyim, beşi öğe olarak sabittir ve kursun geri kalanı bu üçlüyü tek soru olarak sürdürür: kaynakta yazılan bu şey bir değer mi üretiyor?
Yayın Güncelleme
Bu derste
- İfade, deyim ve öğe sınıflarını değer üretme ölçütüyle tanımlar ve birbirinden ayırır
- Ölçüm çekirdeğini koşturarak on sekiz yazımın sınıf dağılımını doğrular
- Aynı işi yapan iki yöntemin imzadan okunan nedenle ayrı sınıflara düşmesini açıklar
İçindekiler
Go müfredatı beş kursla kapandı ve son kursu bir programın kendisi hakkında nelerin bilinebileceğini ölçtü: bir bilgiyi kaynağın mı söylediğini, bir aracın mı, yoksa koşumun mu. Bu müfredat aynı yöne bir adım daha yaklaşır ve doğrudan kaynağa bakar. Rust’ta bir satır yazıldığında o satırın kaderi üçe ayrılır: satır değerlendirilir ve bir değer üretir, değerlendirilir ama hiçbir şey üretmez, ya da hiç değerlendirilmez, yalnız bir ad bildirir. Kurs boyunca sorulacak tek soru budur: kaynakta yazdığın bu şey bir değer mi üretiyor?
Bu soru daha önce iki farklı biçimde soruldu. Java müfredatının ilk kursu derleyicinin kaynağa neyi eklediğini ölçtü — kaynakta yazılı olmayan bir şeyin derleme sırasında nasıl tamamlandığını. Go müfredatının ilk kursu ise bir şeyin yazılmamasının karşılığını ölçtü — dilin bunu tanımlı mı bıraktığını, reddettiğini mi, yoksa açıkta mı bıraktığını. Bu kurs ikisinden de ayrılır: burada eksik hiçbir şey yok, mesele kaynakta yazılı olanın değerlendirilirken ne ürettiğidir.
Bu üçlü sınıflandırma bir tasarım hedefi olarak okunmalıdır, üç kategoriyi ezbere sıralayan
bir liste olarak değil: aynı işi kaynakta üç sınıftan hangisiyle yazdığın, o satırın nerede
kullanılabileceğini belirliyor. Bir ifadeyi bir değişkene bağlayabilir, bir işleve
geçirebilir ya da başka bir ifadenin içine gömebilirsin; bir deyim bunların hiçbirine uygun
değildir, çünkü elinde ()ten başka bir şey yoktur. Kursun ilerleyen dersleri bu sınırın
nerede işe yaradığını — ve nerede bir yazım hatasına dönüştüğünü — tek tek gösterecek.
İfade, Deyim, Öğe
Üç yanıt vardır ve kurs boyunca bu üç ad değişmez.
| Sınıf | Tanım |
|---|---|
| ifade (expression) | Değerlendirilir ve () dışında bir değer üretir |
| deyim (statement) | Değerlendirilir ama () üretir — ya da hiç değerlendirilmez |
| öğe (item) | Yalnız bildirir; kendisi değerlendirilmez, değer kullanınca doğar |
Bu üçlünün ortasında görünen () bir boşluk değildir — Temel Tipler dersinde ayrıca ele
alınacak, ama şimdiden söylenmesi gereken şu: () de bir tiptir ve tek bir değeri vardır.
Bir deyim “hiçbir şey üretmiyor” değil, bu tek değerli tipin değerini üretiyor. if, match,
loop ve blok Rust’ta birer değerdir; noktalı virgül bir değeri atar; fn, struct,
enum, mod ve const ise hiç değerlendirilmez, yalnız derleyiciye bir ad tanıtır. Bu üç
yol var olduğu için soru burada bir kurs taşıyabiliyor.
Programlama Temelleri kursu (M01/K02) if yazımını Python’la kurdu; orada if bir deyimdi ve
bir değer üretmezdi. Bu kavram burada tekrar kurulmaz — tek cümlelik bu atıf, üç sınıfın neden
Rust’ta ayrı bir eksen oluşturduğunu göstermek içindir: aynı yazım her dilde aynı sınıfa
düşmüyor.
Ölçüm Çekirdeği
Kursun geri kalanında kullanılan çekirdek ifade.rsdir. On sekiz ayrı yazımı aynı çerçevede
koşturur, her birini bir gözlem defterine yazar ve sonunda sınıf başına toplamı basar.
Gözlem defterinin kendisi bir demet (tuple) dizisidir — her satır adı, sınıfı ve değeri üç
ayrı metin parçası olarak bir arada tutar. Demet, farklı tipteki birkaç değeri tek bir birim
gibi taşımanın en doğrudan yoludur; burada üç alanın da metin olması bir zorunluluk değil,
tablonun tek biçimde yazdırılabilmesi için yapılmış bir seçimdir. Gözlem defteri dilin bir
parçası değildir — kod okunurken buna dikkat edilmelidir.
RD1. gozle işlevi ve defter demeti (String, String, String üçlüsü) çekirdeğin
kendi tasarım kararıdır, Rust’ın bir düzeneği değildir. Bir yazımın hangi sınıfa girdiğine
karar veren şey her satırın kaynaktaki biçimidir; gozle çağrısı yalnız bu kararı bir
tabloya kaydeder ki on sekiz satır tek koşumda karşılaştırılabilsin.
// ifade.rs — kaynakta yazilan her sey uc siniftan birine girer:
// deger ureten (ifade), () ureten ya da hic degerlendirilmeyen (deyim),
// yalniz bildiren (oge).
use std::collections::BTreeMap;
use std::collections::HashSet;
const SINIR: i32 = 100;
struct Nokta {
x: i32,
y: i32,
}
enum Renk {
Kirmizi,
Yesil,
}
mod olcu {
pub fn birim() -> i32 {
3
}
}
fn iki_kat(x: i32) -> i32 {
x * 2
}
fn gozle(defter: &mut Vec<(String, String, String)>, ad: &str, sinif: &str, deger: String) {
defter.push((ad.to_string(), sinif.to_string(), deger));
}
fn main() {
let mut defter: Vec<(String, String, String)> = Vec::new();
// --- ifade: deger ureten yazimlar ---
let a = 7;
let b = if a > 5 { "buyuk" } else { "kucuk" };
gozle(&mut defter, "if bir deger olarak", "ifade", b.to_string());
let renk = if a > 5 { Renk::Yesil } else { Renk::Kirmizi };
let ad = match renk {
Renk::Kirmizi => "kirmizi",
Renk::Yesil => "yesil",
};
gozle(&mut defter, "match bir deger olarak", "ifade", ad.to_string());
let blok = {
let ic = iki_kat(a);
ic + 1
};
gozle(&mut defter, "blok bir deger olarak", "ifade", blok.to_string());
let mut sayac = 0;
let dongu = loop {
sayac += 1;
if sayac == 4 {
break sayac * 10;
}
};
gozle(&mut defter, "loop break ile deger dondurur", "ifade", dongu.to_string());
let n = Nokta { x: 1, y: 2 };
gozle(&mut defter, "yapi soz dizimi bir deger uretir", "ifade", format!("x={} y={}", n.x, n.y));
let v = vec![1, 2, 3, 4];
gozle(&mut defter, "vec makrosu bir deger uretir", "ifade", format!("uzunluk={}", v.len()));
let toplam: i32 = v.iter().map(|x| iki_kat(*x)).sum();
gozle(&mut defter, "yineleyici zinciri bir deger uretir", "ifade", toplam.to_string());
let mut kume: HashSet<i32> = HashSet::new();
let ilk = kume.insert(5);
let ikinci = kume.insert(5);
gozle(&mut defter, "kume ekleme bir deger dondurur", "ifade", format!("{} sonra {}", ilk, ikinci));
// --- deyim: () ureten ya da hic degerlendirilmeyen yazimlar ---
let noktali = {
iki_kat(2);
};
gozle(&mut defter, "noktali virgulle biten blok", "deyim", format!("{:?}", noktali));
let mut d = 1;
let atama = { d = d + 8; };
gozle(&mut defter, "atamanin kendisi", "deyim", format!("{:?} (d={})", atama, d));
let mut liste = Vec::new();
let itme = liste.push(d);
gozle(&mut defter, "diziye itme", "deyim", format!("{:?} (uzunluk={})", itme, liste.len()));
let mut kez = 0;
let dongu_degeri = for _ in 0..3 {
kez += 1;
};
gozle(&mut defter, "for dongusu", "deyim", format!("{:?} (kez={})", dongu_degeri, kez));
let yazim = print!("");
gozle(&mut defter, "yazdirmanin degeri", "deyim", format!("{:?}", yazim));
// --- oge: yalniz bildiren yazimlar; degerleri kullanildiklarinda dogar ---
gozle(&mut defter, "fn bildirimi", "oge", format!("bildirim; cagrisi {} verdi", iki_kat(3)));
gozle(&mut defter, "struct bildirimi", "oge", format!("bildirim; ornegi x={} verdi", n.x));
gozle(&mut defter, "enum bildirimi", "oge", "bildirim; varyanti yesil verdi".to_string());
gozle(&mut defter, "mod bildirimi", "oge", format!("bildirim; icindeki cagri {} verdi", olcu::birim()));
gozle(&mut defter, "const bildirimi", "oge", format!("bildirim; okunmasi {} verdi", SINIR));
// --- rapor ---
println!("{:<40}{:<8}{}", "gozlem", "sinif", "deger");
for (ad, sinif, deger) in &defter {
println!("{:<40}{:<8}{}", ad, sinif, deger);
}
let mut sayim: BTreeMap<&str, i32> = BTreeMap::new();
for (_, sinif, _) in &defter {
*sayim.entry(sinif.as_str()).or_insert(0) += 1;
}
println!();
for (sinif, adet) in &sayim {
println!("{:<8}{}", sinif, adet);
}
println!("toplam {}", defter.len());
}
gozlem sinif deger if bir deger olarak ifade buyuk match bir deger olarak ifade yesil blok bir deger olarak ifade 15 loop break ile deger dondurur ifade 40 yapi soz dizimi bir deger uretir ifade x=1 y=2 vec makrosu bir deger uretir ifade uzunluk=4 yineleyici zinciri bir deger uretir ifade 20 kume ekleme bir deger dondurur ifade true sonra false noktali virgulle biten blok deyim () atamanin kendisi deyim () (d=9) diziye itme deyim () (uzunluk=1) for dongusu deyim () (kez=3) yazdirmanin degeri deyim () fn bildirimi oge bildirim; cagrisi 6 verdi struct bildirimi oge bildirim; ornegi x=1 verdi enum bildirimi oge bildirim; varyanti yesil verdi mod bildirimi oge bildirim; icindeki cagri 3 verdi const bildirimi oge bildirim; okunmasi 100 verdi deyim 5 ifade 8 oge 5 toplam 18
RD2. Koşum belirlenimcidir: aynı kaynak her koşumda aynı on sekiz satırı, aynı sırayla ve aynı sınıf dağılımıyla üretir. Hiçbir satır bir dosya sisteminden, saatten ya da rastgele üreteçten okumuyor — hepsi sabit bir kaynaktan hesaplanıyor. On sekiz yazımın 8’i ifade, 5’i deyim, 5’i öğedir; bu sayı kursun geri kalanında değişmez, yalnız her ders kendi kesitini alır.
Sınıf başına toplamı hesaplayan son blok bir sıralı sözlük (BTreeMap) kullanıyor, karma
sözlük (HashMap) değil. Bu seçim rastgele değil: bir karma sözlüğün anahtarları arasında
gezinme sırası koşumdan koşuma değişebilir, üç sınıfın toplamı doğru çıksa bile satırların
hangi sırayla basılacağı garanti edilmez. Sıralı sözlük anahtarları her zaman aynı
sırada — burada alfabetik olarak deyim, ifade, öğe — dolaştığı için çıktı metni de
belirlenimci kalıyor. Bu, kursun ortama bağlı veri konusundaki kuralının en somut örneğidir:
bir koleksiyonun sırası bir gösterim aracı olarak kullanılacaksa, o sıranın güvence taşıyan
bir yapıdan gelmesi gerekir. Bu ayrım Sıralı ve Küme Yapıları dersinde kendi başına bir konu
olacak; burada yalnız çekirdeğin kendi çıktısını neden güvenle koşum sonrası koşuma
karşılaştırılabildiğini açıklıyor.
Sonuçların Okunuşu
Sekiz ifade satırının ortak noktası, hepsinin bir let bağlamasının sağ tarafında durup bir
değer üretmesidir: if/match bir dal seçer ve o dalın değerini verir, blok kendi son
satırını verir, loop yalnız break’e verdiği değeri verir, yapı sözdizimi ve vec makrosu
yeni bir değer kurar, yineleyici zinciri bir toplam biriktirir, küme ekleme ise bool
döndürür. Beş deyim satırı ise tam tersini gösteriyor: noktalı virgülle kapanan blok, bir
atama, Vec::push, for döngüsü ve boş bir yazdırma — beşi de () basıyor, çünkü beşi de
“bir iş yap” diyor, “bir değer ver” demiyor. Beş öğe satırı ayrı bir eksende duruyor: fn,
struct, enum, mod ve const bildirimlerinin kendisi tabloya hiç girmiyor, tabloya giren
şey bu bildirimlerin kullanımından doğan değer — iki_kat(3)’ün döndürdüğü 6, Nokta
örneğinin alanı, olcu::birim()’in çağrısı, SINIR’ın okunması. Bir bildirim yazıldığı anda
hiçbir şey üretmiyor; yalnız bir ad tanıtıyor ve o ad ilerideki bir satırda kullanılınca değer
doğuyor.
RD3. iki_kat öğesi çekirdekte üç ayrı yerde kullanılıyor: bir kez blok içinde
(ic + 1 hesabına girecek değeri üretmek için), bir kez yineleyici zincirinde her elemana
uygulanmak için, bir kez de “fn bildirimi” satırında doğrudan çağrılıp sonucu tabloya
yazılmak için. Üçünde de iki_kat’ın kendisi değişmiyor — değişen, her çağrıya verilen
argüman ve o çağrının döndürdüğü değer. Bu, öğenin “değersiz” olmasının ne anlama geldiğini
somutlaştırıyor: bildirim tek, ama kullanım sayısı ve her kullanımın ürettiği değer serbest.
Bu üç grup arasında sınıf sayısı eşit değildir ve eşit olması da beklenmiyor: kaynağın çoğu
bir değer üretmek için yazılıyor (8), geri kalanı ya bir yan etki için (5) ya da bir ad
tanıtmak için (5) yazılıyor. Kursun geri kalanı bu üç grubu tek tek derinleştirecek — if,
match, loop ve döngülerin tamamı Denetim Akışı dersinde, atama ve bağlama Değişkenler ve
Değişmezlik dersinde, bildirimlerin kaynak üzerindeki payı Kurulum ve Paket Yöneticisi
dersinde ayrıca ele alınacak. Bu derste kalan tek soru, sınıfın nereden okunduğudur.
Sınırlayıcı Ölçüm: Sınıf İmzadan Okunur
Tabloda yan yana duran iki satır bu sorunun yanıtını taşıyor: “kume ekleme bir deger
dondurur” (ifade, true sonra false) ile “diziye itme” (deyim, () (uzunluk=1)). İkisi
de aynı işi yapıyor gibi görünür — bir koleksiyona bir eleman eklemek. Ama biri koşumda bir
değer üretti, öbürü üretmedi. Fark, işin doğasında değil, çağrılan yöntemin imzasında
yatıyor.
HashSet::insert hem kümeyi değiştirir hem de eklemenin gerçekten yeni bir öğe kattığını
söyleyen bir mantıksal değer döndürür. Çekirdekte 5 iki kez eklenmiş: ilk çağrı kümeye yeni
bir öğe kattığı için true, ikinci çağrı zaten var olan 5i tekrar eklemeye çalıştığı için
false döndürüyor — tablodaki true sonra false bu iki çağrının yan yana konmuş sonucudur.
Vec::push ise hiçbir zaman böyle bir bilgi vermiyor: bir diziye eleman eklemek her zaman
başarılı olduğu için döndürülecek bir mantıksal değer yok, imza yalnız () veriyor.
Bu, “ekleme” diye adlandırılan tek bir işin iki ayrı Rust yöntemine iki ayrı sınıf verdiğini
gösteriyor. Sınıfı belirleyen okuyucunun işin ne olduğuna dair sezgisi değil, derleyicinin
gördüğü dönüş tipidir: dönüş tipi () ise deyim, başka bir tip ise ifade. Bu ölçüt biçimsel
ve mekanik olduğu için, bir yöntemin çağrısına bakmadan, yalnız imzasına bakarak hangi sınıfa
gireceği önceden söylenebilir — Option ya da bool döndüren bir değiştirici her zaman
ifadedir, yalnız () döndüren bir değiştirici her zaman deyimdir. Koleksiyonlar dersinde bu
ayrım Vec::popin Option döndürmesiyle bir kez daha karşımıza çıkacak.
Bu, kursun tasarım hedefi başlığı altında durmasının nedenini de açıklıyor: sınıf bir
çalışma zamanı gözlemi değil, bir imza kararıdır ve imza kaynakta koşum yapılmadan
okunabilir. Bir okuyucu HashSet::insertin dönüş tipine bakarak, hiçbir örneği koşturmadan,
onun bir ifade olduğunu söyleyebilir. Standart kitaplığın kendi tasarımı bu sınıflandırmayı
önceden belirliyor; ders boyunca ölçülen şey, bu belirlemenin kaynağı okurken nasıl bir
farkla karşımıza çıktığıdır.
Özet
- Kaynakta yazılan her şey üç sınıftan birine girer: ifade bir değer üretir, deyim
()üretir ya da hiç değerlendirilmez, öğe yalnız bir ad bildirir ve değeri kullanımdan doğar. ()boşluk değildir; tek değerli bir tiptir ve deyimin ürettiği şey budur.- Ölçüm çekirdeği on sekiz yazımı tek koşumda sınıflandırır: 8’i ifade, 5’i deyim, 5’i öğe — bu sayı kursun geri kalanında değişmez.
- Aynı iş (ekleme) iki ayrı yönteme iki ayrı sınıf verebilir; sınıf işin doğasından değil, yöntemin dönüş tipinden — imzasından — okunur.
Sonraki Adım
Üç sınıf artık tanımlı, ama bir kaynağın gerçekten çalışabilmesi için bunlardan çok daha küçük, çok daha zorunlu bir küme var: bir programın en az ne yazması gerekir. Sıradaki ders bu en küçük kümeyi sorar — kaynağın kendisinde durmayan, ayrı bir dosyada tutulan bir parça değişince kaynak hiç değişmeden çıktının nasıl değiştiğini gösterir.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.