Ders 02 / 12
Kurulum ve Paket Yöneticisi
Bir kaynağın çalışabilir bir ikiliye dönüşmesi için yazılması zorunlu üç parça vardır — giriş noktası, kaynak kökü, bildirim dosyası; üçü de öğedir, hiçbiri tek başına bir değer üretmez, ama üçü birlikte tek bir çıktı üretir ve bildirim dosyasındaki bir değişiklik kaynağa hiç dokunmadan o çıktının adını değiştirebilir.
Yayın Güncelleme
Bu derste
- Bir kaynağın çalışabilmesi için zorunlu üç parçayı (giriş noktası, kaynak kökü, bildirim dosyası) sayar
- Bu üç parçanın öğe sınıfına girdiğini, kendilerinin değil kullanımlarının değer ürettiğini gösterir
- Bildirim dosyasının kaynağın dışında durduğunu, kaynağı değiştirmeden çıktı adını değiştirerek kanıtlar
İçindekiler
Önceki ders üç sınıfı tanımladı ve on sekiz yazımı bu sınıflara ayırdı; hiçbiri tek başına bir programın çalışabilmesi için yeterli değildi, çünkü sorulan soru zaten farklıydı. Bu ders soruyu daraltıyor: bir kaynak metninin, hiçbir eksik olmadan, çalışan bir ikiliye dönüşmesi için en az ne yazılmalı? Yanıt üç parçadır ve üçü de önceki dersten tanıdık bir sınıfa giriyor — öğe. Hiçbiri kendi başına bir değer üretmiyor, ama üçü bir arada tek bir çıktı üretiyor: çalıştırılabilir bir program.
Bu soru önceki dersteki sorudan küçük görünür ama bağımsız değildir. Önceki ders, kaynakta yazılı olan bir şeyin değerlendirilirken ne ürettiğini sordu; bu ders ise değerlendirmenin hiç başlayabilmesi için kaynağın etrafında hangi çerçevenin durması gerektiğini soruyor. İfade, deyim ve öğe ayrımı yalnız Rust kaynağının içinde geçerli bir sınıflandırmadır; bu ders o kaynağın nereden başladığını ve onu saran veriyle ilişkisini gösteriyor.
Üç Zorunlu Öğe
Bir kaynağın çalışabilir bir ikiliye dönüşmesi için gereken üç parça şunlardır: giriş
noktası (programın başlayacağı yeri işaretleyen fn main), kaynak kökü (derleyicinin
okumaya başladığı dosyanın kendisi) ve bildirim dosyası (paketin adını ve hangi dil
sürümüyle derleneceğini tutan veri dosyası). Standart bir proje düzeni bunu şöyle ayırır:
proje/
├── Cargo.toml
└── src/
└── main.rs
# Cargo.toml icerigi
[package]
name = "hesaplayici"
version = "0.1.0"
edition = "2021"
Bu üçü, önceki derste kurulan üç sınıftan öğeye giriyor: hiçbiri tek başına
değerlendirilmiyor, yalnızca bir şey bildiriyor. fn mainin kendisi bir işlev bildirimidir
— tıpkı önceki dersteki iki_kat gibi. Kaynak kökü bir dosyadır, dosyanın kendisi
“koşulmaz”, yalnız içindeki bildirimler derleyiciye tanıtılır. Bildirim dosyası ise Rust
kaynağı bile değildir; TOML biçiminde bir veridir ve paket yöneticisi tarafından okunur.
Dizin ağacındaki ayrım gelişigüzel değildir: src/ altında yalnız kaynak dosyaları durur,
Cargo.toml ise projenin köküne, src/ın dışına konur. Bu yerleşim, “kaynak” ile “kaynak
hakkındaki veri”yi aynı klasörde karıştırmama kararıdır — bir araç kaynak dosyalarının
tamamını taramak isterse yalnız src/ dizinine bakması yeterlidir, bildirim dosyasını ayrı
bir mantıkla okur. Kaynak kökü konumuna gelince: burada tek dosyalık bir kaynak
kullanıyoruz ve onu doğrudan derleyiciye veriyoruz, çünkü bu kursta paket yöneticisi hiç
çalıştırılmıyor. Gerçek bir projede kaynak kökü src/main.rsdir ve derleyici oradan
başlayıp başka dosyalara mod bildirimleriyle ulaşır — bu genişleme Modüller ve Kasalar
dersinin konusu; burada yalnız kökün tek bir dosya olduğu ve derlemenin oradan
başladığı bilgisi yeterli.
RD4. fn mainin “kullanılması” önceki dersteki bir işlevin kullanılmasından farklıdır:
iki_kat programın kendi içinden çağrılıyordu, main ise hiçbir satır tarafından
çağrılmıyor — onu çağıran, işletim sistemi sürecin başlangıcında derlenmiş ikiliyi
çalıştırdığında devreye giren çalışma zamanı ortamıdır. Bildirim (öğe) aynı bildirim, ama
çağıran taraf kaynağın dışında.
Bu, kaynağın kendi başına eksik olduğu anlamına gelmiyor; kaynak kaydı kaynak kökünde
tamamlanmış durumda, yalnız o kaydın hangi anda değere dönüşeceğine kaynak kendisi karar
veremiyor. Bir kaynakta iki_kat, gozle gibi başka öğeler de bulunabilir — hepsi bildirim
olarak durur, hiçbiri kendiliğinden koşmaz. Bunlardan yalnız biri, main adını taşıyan,
işletim sistemi tarafından tanınan giriş noktasıdır; diğerleri ancak mainin gövdesinden ya
da mainin çağırdığı başka bir öğeden ulaşılırsa değer üretir. Bu, önceki dersin “öğeler
ölçüde değersizdir” bulgusunun proje düzeyindeki karşılığıdır: bir kaynak ağacı istediği
kadar öğe bildirsin, giriş noktasından ulaşılamayan bir öğe hiçbir zaman koşmaz.
RD5. Bildirim dosyası hiç Rust kaynağı değildir, bu yüzden “ifade / deyim / öğe”
sınıflandırması ona gerçek anlamda uygulanmaz — o sınıflandırma yalnız Rust kaynağı için
tanımlıdır. Ama işlevsel olarak aynı yere düşüyor: bildirim dosyası da kendisi
“çalıştırılmıyor”, yalnız paket yöneticisinin okuduğu bir veri sağlıyor ve nihai çıktıya
adıyla katkıda bulunuyor. Bildirim dosyasının biçimi de bu farkı görünür kılıyor:
name = "hesaplayici" bir anahtar-değer eşleşmesidir, bir çağrı ya da bir dallanma değil —
TOML’de değerlendirilecek bir kod yolu yok, yalnız okunacak bir veri var.
Bu üç parçanın birlikte oluşturduğu bütüne paket denir: bildirim dosyasının işaret ettiği kaynak ağacı ile bildirim dosyasının kendisi, tek bir dağıtım ve derleme birimini oluşturur. Paket yöneticisinin adı da buradan gelir — yönettiği şey tek bir kaynak dosyası değil, kaynak ile kimliğin bir arada oluşturduğu bütündür. Bir paketin sınırı, dosya sisteminde bildirim dosyasının bulunduğu dizindir; o dizinin altındaki her kaynak dosyası aynı pakete ait sayılır.
TOML’ün kendisi de bu ayrımı destekleyecek biçimde tasarlanmış bir veri dilidir: yalnız
anahtar = değer satırları ve köşeli parantezle açılan bölüm başlıkları ([package] gibi)
içerir. Dallanma, döngü, fonksiyon çağrısı yoktur — bir bildirim dosyası okunurken
“çalıştırılacak” bir şey değil, dümdüz okunacak bir kayıt kümesidir. Bu, Rust kaynağının
kendisiyle taban tabana zıttır: kaynakta değerlendirilecek ifadeler ve deyimler vardır,
bildirim dosyasında yalnız veri vardır.
Kaynak kökü ile giriş noktası arasındaki fark da benzer bir ayrımdır ve karıştırılmamalıdır.
Kaynak kökü bir dosyadır (derleyicinin okumaya başladığı yer); giriş noktası ise o
dosyanın içindeki belirli bir öğedir (fn main, çalışmanın başlayacağı işlev). Küçük bir
programda ikisi aynı dosyada durabilir, ama kavramsal olarak ayrıdır: bir kaynak kökü, içinde
hiç fn main olmadan da başka dosyalara mod ile bağlanan yardımcı bildirimler taşıyabilir
— önceki derste bunu mod olcu ile gördük. Bir paketin çalıştırılabilir olması için gereken
şey kaynak kökünün kendisi değil, kaynak ağacının bir yerinde fn mainin bulunmasıdır.
Bu ders boyunca gösterilen dizin ağacı ve bildirim dosyası metin bloğu çalıştırılmıyor ve hiçbir sayı iddiası taşımıyor; yalnızca gerçek bir projenin nasıl göründüğünü belgeliyor. Kursun geri kalanında koşan her örnek, paket yöneticisinden bağımsız, doğrudan derleyiciyle çalışacak biçimde kurgulanacak — bu, ağ erişimi gerektirmeyen bir ölçüm ortamı kurmanın sonucu, dilin kendisiyle ilgili bir kısıtlama değil.
Giriş noktasının tek başına ne yaptığını görmek için en küçük hâliyle koşturalım.
// programimiz.rs — bir kaynagin calisabilmesi icin en az yazilmasi gereken sey
fn main() {
println!("dogrulama-anahtari={}", 7 * 6);
}
dogrulama-anahtari=42
Bu kaynakta tek bir öğe var: fn main. Kaynağın kendisi hiçbir değer üretmiyor —
derleyiciye “programın başlangıcı burası” diyor. Değer, program koşturulduğunda doğuyor,
tıpkı önceki dersteki iki_kat(3) çağrısının değeri fonksiyon çağrıldığında
doğurmasına benzer biçimde. Fark, çağıran tarafın kaynağın dışında olması.
Gövdenin içindeki yazdırma çağrısı kendisi önceki dersten tanıdık bir sınıfa giriyor: bir
deyim. Önceki dersin tablosunda “yazdırmanın değeri” satırı tam olarak bunu gösterdi —
yazdırma () döndürür, kendisi bir değer üretmez. Burada değişen tek şey, o deyimin artık
tek başına değil, fn main adlı bir öğenin gövdesinin içinde durmasıdır: öğe kendisi değer
üretmiyor, ama gövdesindeki deyim çalıştırıldığında bir yan etki (bir satırın ekrana
basılması) üretiyor. İfade, deyim ve öğe sınıfları böylece iç içe geçebiliyor — bir öğenin
gövdesi deyimlerden ve ifadelerden oluşabiliyor, ama öğenin kendisi hâlâ öğe kalıyor.
Kaynak kökünün derleyicinin okumaya başladığı tek dosya olması bir zorunluluktur: fn main
içermeyen bir dosya derlenip diske yazılabilir (önceki derste mod içindeki olcu işlevi
tam olarak bunu yaptı), ama işletim sisteminin doğrudan çalıştırabileceği bir ikili elde
etmek için bir yerde bir fn main bulunması şarttır. Bu, kursun geri kalanında tekrar
görülmeyecek tek bir olgudur: giriş noktası olmayan bir kaynak koleksiyonu derlenebilir,
ama çalıştırılabilir değildir.
Bu üç zorunlu parçanın birbirinden ayrılması bir rastlantı değil, kendisi bir tasarım kararıdır — kursun başlığıyla aynı eksende durur. Kaynağı, kaynağın kimliğinden (ad, sürüm, dil sürümü) ayırmak, ikisinin bağımsız değişebilmesini sağlıyor: bir projenin mantığı hiç değişmeden adı değişebilir, ya da adı hiç değişmeden mantığı değişebilir. İki bilgiyi aynı dosyada tutan bir tasarım bunu sağlayamazdı — kaynağı okuyan her araç, önce kimlik bilgisini kaynağın neresinde arayacağını bilmek zorunda kalırdı.
Sınırlayıcı Ölçüm: Bildirim Dosyası Kaynağın İçinde Değildir
Bildirim dosyasının kaynaktan ayrı durmasının somut bir sonucu var: kaynağı hiç değiştirmeden,
yalnız bildirim dosyasındaki bir alanı değiştirerek çıktının adını değiştirebilirsin.
Paket yöneticisi normal bir projede ikilinin adını bildirim dosyasının name alanından
türetir. Aynı ilkeyi, paket yöneticisi çalıştırmadan, doğrudan derleyicinin çıktı adlandırma
seçeneğiyle gösterelim — kaynak (programimiz.rs) hiç değişmiyor, yalnız hedef ad değişiyor.
isim_a="hesaplayici" isim_b="hesap-araci" rustc --edition 2021 -o "$isim_a" programimiz.rs rustc --edition 2021 -o "$isim_b" programimiz.rs echo "cikti_a: $(./$isim_a)" echo "cikti_b: $(./$isim_b)" if [ "$isim_a" != "$isim_b" ]; then echo "ikili_adlari_farkli: evet" else echo "ikili_adlari_farkli: hayir" fi
cikti_a: dogrulama-anahtari=42 cikti_b: dogrulama-anahtari=42 ikili_adlari_farkli: evet
RD6. Bu ölçüm paket yöneticisini çalıştırmıyor — bu derste de hiçbir kod bloğunda
koşturulmuyor. Gösterilen şey, paket yöneticisinin ikili adını bir veri alanından türetme
ilkesidir: rustcin -o seçeneği burada bildirim dosyasındaki name alanının yerini
tutuyor. Aynı kaynak dosyası iki kez, iki farklı hedef adla derlendi ve ikisi de birebir aynı
çıktıyı verdi; değişen tek şey ikilinin adı oldu.
Bu sonuç, önceki dersin “sınıf imzadan okunur” bulgusuyla aynı ailededir: orada bir
yöntemin sınıfı çağrı yerine değil imzasına bakılarak belirleniyordu, burada bir ikilinin
adı kaynağa değil bildirim dosyasına bakılarak belirleniyor. İki durumda da kaynağın kendisi
bir bilgiyi taşımıyor; o bilgi kaynağın dışında bir yerde duruyor ve kaynak o bilgiden
habersiz çalışıyor. programimiz.rs dosyası “hesaplayici” ya da “hesap-araci” adında bir
ikiliye dönüşeceğini hiçbir satırında bilmiyor.
Ölçümdeki cikti_a ve cikti_b satırlarının birebir aynı olması da kendi başına bir
gözlemdir: aynı kaynak, aynı derleyici seçenekleriyle iki kez derlendiğinde iki ayrı ikili
üretiyor ama davranışları hiç ayrışmıyor. Bu, önceki dersteki belirlenimcilik bulgusunun
derleme düzeyindeki karşılığıdır — orada aynı kaynağın her koşumda aynı on sekiz satırı
ürettiği gösterilmişti, burada aynı kaynağın her derlemede aynı davranışı ürettiği
gösteriliyor. Değişen tek şey, derlemenin dışına bırakılan bir isimdir.
Bu bağımsızlık tek yönlü değil. Derleme tamamlandıktan sonra üretilen ikili, kaynaktan da bağımsızlaşır — kaynak dosyası silinse bile, daha önce üretilmiş ikili çalışmaya devam eder.
rustc --edition 2021 -o ikiliornek programimiz.rs mv programimiz.rs programimiz_yedek.rs echo "calisma: $(./ikiliornek)" if [ -f programimiz.rs ]; then echo "kaynak_hala_ayni_adda: evet" else echo "kaynak_hala_ayni_adda: hayir" fi
calisma: dogrulama-anahtari=42 kaynak_hala_ayni_adda: hayir
RD7. İkili, derlemenin sonucudur; kaynağın kendisi değil, kaynağın belirli bir anda değerlendirilmiş hâlidir. Kaynak dosyası taşınsa ya da silinse bile ikili bunu bilmez, çünkü bağ derleme anında kurulmuş ve o andan sonra ikili kendi başına bir dosyadır. Bu, giriş noktası – kaynak kökü – bildirim dosyası üçlüsünün nihai amacını gösteriyor: üçü bir arada, kendi kendine yeten, kaynaktan bağımsız tek bir çıktı üretiyor. Bu, dağıtım pratiğinde sık karşılaşılan bir durumun temelidir: bir sunucuya yalnız derlenmiş ikili taşınır, kaynak hiç oraya gitmez — ikili kendi başına çalışabildiği için bu taşımanın hiçbir sakıncası yoktur.
Bu ayrımın somut bir pratik değeri var: aynı hesaplama mantığını taşıyan bir kaynak dosyası,
bir bağlamda iç bir araç olarak “hesaplayici” adıyla, başka bir bağlamda dağıtılan bir
paketin parçası olarak bambaşka bir adla derlenebilir. Kaynak hiç kopyalanıp
değiştirilmeden, yalnız çevresindeki bildirim değiştirilerek iki ayrı kimlik elde edilir —
yukarıdaki ölçüm bunu birebir gösteriyor: programimiz.rs bir kez bile açılıp
düzenlenmeden iki farklı adla derlendi.
Bu ayrımın pratik bir sonucu var: bir projenin adını değiştirmek, kaynak metninde tek bir karakter değiştirmeden yapılabilecek bir işlemdir. Kaynak kökü ile bildirim dosyası aynı projenin iki ayrı görünürlük katmanını taşıyor — biri ne çalıştığını, öbürü projenin nasıl adlandığını ve hangi dil sürümüyle derleneceğini söylüyor. Bu iki katman birbirine karışmıyor: bildirim dosyasını okumadan kaynağı okuyabilir, kaynağı okumadan bildirim dosyasını okuyabilirsin ve ikisi de öbürü hakkında hiçbir varsayımda bulunmuyor.
Aynı ayrım tersinden de geçerlidir: aynı kaynak kökü, iki ayrı bildirim dosyasıyla iki ayrı
projenin parçası olabilir. Bir kaynak dosyasının hangi projeye ait olduğunu belirleyen şey
dosyanın içeriği değil, onu hangi bildirim dosyasının işaret ettiğidir. Bu, programimiz.rs
örneğinde görüldüğü gibi, kaynağın taşınabilirliğinin bir sonucudur: dosyanın içeriği sabit
kalırken çevresindeki bildirim değişebiliyor ve bu iki değişim birbirinden bağımsız.
Bildirim dosyasının taşıdığı diğer alanlar da (yukarıdaki örnekte görülen sürüm ve dil
sürümü satırları) aynı ilkeye uyuyor: kod bloğunda durur, kaynağın kendisine hiç
karışmıyor. Derleyiciye bu derste doğrudan --edition 2021 seçeneği verildi çünkü paket
yöneticisi hiç koşturulmuyor; gerçek bir projede bu bilgiyi paket yöneticisi bildirim
dosyasından okuyup derleyiciye kendisi iletir. Kaynak dosyası bu bilgilerin hiçbirini
taşımaz — yalnız ne yaptığını bilir, çevresindeki bildirimin neyi taşıdığını bilmez.
Bu ders, önceki dersteki “öğe” sınıfının yalnız tek bir bildirim satırıyla sınırlı olmadığını gösterdi. Bir dosyanın kendisi (kaynak kökü) ve bir projenin kimliği (bildirim dosyası) da aynı mantıkla okunabiliyor: ikisi de kendi başına bir değer üretmiyor, ikisi de yalnız bir çerçeve kuruyor ve o çerçevenin içinde çalışan kod bir çıktı üretiyor. Sıradaki ders bu çerçevenin içine, giriş noktasının gövdesine iniyor ve orada ilk yazılan satırların çoğunun bir ada bir değer bağlamak olduğunu ele alıyor.
Özet
- Bir kaynağın çalışabilir bir ikiliye dönüşmesi için üç parça zorunludur: giriş noktası, kaynak kökü, bildirim dosyası.
- Üçü de öğedir: hiçbiri kendisi bir değer üretmez, üçü birlikte tek bir çıktı — çalışan bir program — üretir.
- Giriş noktasını “kullanan” taraf kaynağın kendisi değil, işletim sisteminin süreç başlatma anıdır.
- Bildirim dosyası Rust kaynağı değildir ve kaynağın dışında durur; kaynağı hiç değiştirmeden yalnız bildirim dosyasını değiştirerek çıktının adı değiştirilebilir.
- Kaynak kökü ile bildirim dosyası birbirinden habersiz iki ayrı katmandır: biri neyin çalıştığını, öbürü projenin nasıl adlandığını taşır.
- Derlenmiş ikili, üretildikten sonra kaynaktan bağımsızlaşır: kaynak dosyası silinse ya da taşınsa bile, önceden üretilmiş ikili çalışmaya devam eder.
Sonraki Adım
Giriş noktasının içinde artık bir gövde yazılabilir, ama o gövdenin ilk satırı genellikle bir ad kurmaktır — bir değeri bir adla bağlamak. Sıradaki ders bu bağlamanın kendisinin hangi sınıfa girdiğini sorar ve aynı adın üç kez yeniden bağlanmasıyla bir kez bağlanıp iki kez değiştirilmesi arasındaki farkı, ikisi de aynı sonucu verse bile, koşturarak gösterir.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.