Ders 04 / 12
Temel Tipler
Tam sayı, kayan noktalı, mantıksal, karakter ve demet — beş temel tip, birer ifade olarak koşturulduğunda beş ayrı değer üretir; ama altıncı bir tip daha vardır — (), tek bir değeri olan tip — ve bir deyimin ürettiği şeyin yokluk değil, tam olarak bu tipin o tek değeri olduğu, aynı koşumda iki değer yan yana basılarak gösterilir.
Yayın Güncelleme
Bu derste
- Beş temel tipin (tam sayı, kayan noktalı, mantıksal, karakter, demet) bir ifade olarak ürettiği değeri koşturarak gösterir
- Tam sayı taşmasının tanımlı davranışını sarma yöntemiyle, karakterin bir bayt olmadığını uzunluk karşılaştırmasıyla ölçer
- () tipinin tek bir değeri olduğunu ve bir deyimin ürettiği şeyin bu değer olduğunu kanıtlar
İçindekiler
Önceki ders bağlamanın kendisini ele aldı ama bağlanan değerlerin çeşidine girmedi — yalnız
tam sayılar görüldü. Bu ders o boşluğu dolduruyor: Rust’ın beş temel tipi (tam sayı, kayan
noktalı, mantıksal, karakter, demet) birer ifade olarak koşturulduğunda ne üretiyor ve bu
beşin yanında duran altıncı bir tip — () — neden “değersiz” değil, tam tersine kendi
başına bir tip.
Beş temel tipin her biri, önceki dersteki değişmezlik tartışmasının üzerine kurulan sabit bir sınır taşır. Bir tam sayı belirli bir bit genişliğine sahiptir ve bu genişlik alabileceği en büyük ve en küçük değerleri belirler; bir kayan noktalı sayı yaklaşık bir hassasiyet taşır; bir mantıksal değer yalnız iki durumdan birini alabilir; bir karakter tek bir Unicode kod noktasını temsil eder; bir demet sabit sayıda bileşenden oluşur ve bu sayı derleme zamanında bellidir. Bu sınırların her biri bu derste ayrı ayrı ölçülüyor.
Beş Tip, Beş Değer
Aşağıdaki kaynak beş temel tipten birer değer bağlıyor ve her birini yazdırıyor; ardından demedin bileşenlerini yapısöküm (destructuring) ile ayrı adlara ayırıyor.
// temel_tipler.rs — her temel tipin bir ifade olarak urettigi deger
fn main() {
let tam: i32 = -7;
let kayan: f64 = 2.5;
let mantiksal: bool = kayan > 2.0;
let harf: char = 'r';
let ikili: (i32, bool) = (tam, mantiksal);
println!("tam sayi: {}", tam);
println!("kayan noktali: {}", kayan);
println!("mantiksal: {}", mantiksal);
println!("karakter: {}", harf);
println!("demet: ({}, {})", ikili.0, ikili.1);
let (ilk, ikinci) = ikili;
println!("yapisokum ilk: {}", ilk);
println!("yapisokum ikinci: {}", ikinci);
let birim: () = ();
let mut sayac = 0;
let deyim_sonucu = { sayac += 1; };
println!("acikca tipi olan birim: {:?}", birim);
println!("bir deyimin urettigi: {:?}", deyim_sonucu);
println!("ikisi ayni deger mi: {}", birim == deyim_sonucu);
println!("sayac su an: {}", sayac);
}
tam sayi: -7 kayan noktali: 2.5 mantiksal: true karakter: r demet: (-7, true) yapisokum ilk: -7 yapisokum ikinci: true acikca tipi olan birim: () bir deyimin urettigi: () ikisi ayni deger mi: true sayac su an: 1
İlk beş yazdırma satırı, önceki dersteki “ifade bir değer üretir” tanımının beş ayrı tip
üzerinden tekrarıdır: -7 bir tam sayı değeridir, 2.5 bir kayan noktalı değerdir, kayan > 2.0 bir mantıksal değer üretir, 'r' tek bir karakterdir, (tam, mantiksal) iki değeri bir
arada tutan bir demettir. mantiksalın kendisi de dikkat çekiyor: onu üreten şey bir sabit
değil, bir karşılaştırma ifadesidir — Rust’ta karşılaştırma işleçlerinin kendisi de birer
ifadedir ve bool tipinde bir değer döndürür.
Bu beş değerin tip açıklamaları (i32, f64, bool, char, (i32, bool)) burada
bilerek açıkça yazıldı; Rust bunları çoğu zaman sağ taraftaki değerden çıkarabilir, ama
tipin kendisi kaynakta yazılmasa bile hep vardır — her bağlamanın tek bir sabit tipi olur ve
bu tip derleme zamanında belirlenir. tam değişkeni -7 değerini taşıyor ve tipi i32;
Rust’ın varsayılan tam sayı tipi budur, otuz iki bit genişliğinde ve hem negatif hem pozitif
değer taşıyabilir. kayan değişkeni ise f64 tipinde, altmış dört bit genişliğinde bir
kayan noktalı sayıdır ve Rust’ın varsayılan kayan noktalı tipidir. Demedin tipi
(i32, bool) biçiminde yazılıyor — parantez içinde, virgülle ayrılmış, bileşenlerin kendi
tipleri sırayla. Bir demedin tipi yalnız bileşen tipleriyle değil, bileşen sayısıyla da
belirlenir: iki elemanlı bir demet ile üç elemanlı bir demet, bileşenleri aynı olsa bile
farklı tiplerdir.
RD11. let (ilk, ikinci) = ikili; satırı, önceki bölümde görülen ikili.0 /
ikili.1 biçiminden farklı bir erişim yolu kullanıyor: demedin bileşenlerini teker teker
okumak yerine, demedin biçimini bir desende tekrarlayıp iki yeni adı tek satırda
bağlıyor. Bu, sağdaki ikili ifadesinin değerlendirilip demedin sol taraftaki desenle
eşleştirilmesiyle çalışıyor; sol taraf bir demet biçimindeyse, sağ taraf da aynı sayıda
bileşene sahip bir demet olmak zorunda. Desen eşlemenin daha genel biçimi ileride ayrıca
ele alınacak; burada yalnız demede özgü bu tek biçim kullanıldı.
Demedin sabit bileşen sayısı, önceki derste Vec benzeri büyüyebilen bir yapıdan farklı bir
garanti veriyor: ikili iki bileşenli olarak kurulduğu andan itibaren, kaynağın hiçbir
yerinde üçüncü bir bileşen eklenemez ya da birinci bileşen çıkarılamaz. Bu sabitlik derleme
zamanında bilinir ve yapısöküm satırının solundaki desenin sağdaki demetle birebir
sayıca eşleşmesini zorunlu kılar — let (ilk, ikinci, ucuncu) = ikili; gibi bir yazım,
ikili yalnız iki bileşen taşıdığı için derlenmez. Bu, bir demedin tipinin yalnız
bileşenlerin tipleriyle değil sayısıyla da tanımlandığının bir başka görünümü.
Sınırlayıcı Ölçüm: () Değersiz Değil, Tek Değerli
Çıktının son dört satırı bu dersin asıl iddiasını taşıyor. birim adlı bağlama açıkça ()
tipinde tanımlandı ve () değeriyle bağlandı — bu, ()in gerçek bir tip olduğunu, tıpkı
i32 ya da bool gibi bir tip adı taşıdığını gösteriyor. deyim_sonucu ise bambaşka bir
yoldan geldi: bir blok içinde sayacı bir artıran bir deyimin sonucu olarak. İki değer
farklı yollardan üretildi, ama eşitlik sınaması true verdi — çünkü () tipinin tek bir
değeri var ve her iki bağlama da o tek değeri tutuyor.
Bu, “bir deyim değer üretmez” cümlesinin yanlış anlaşılabileceği yeri gösteriyor. Deyim
değer üretmez demek yanıltıcı olurdu; doğrusu, deyim () tipinin tek değerini üretir.
Yokluk ile “tek değerli bir tipin değeri” aynı şey değildir: yokluk bir tipin bile olmaması
demektir, oysa () gerçek bir tiptir, boyutu tanımlıdır ve fonksiyon imzalarında dönüş
tipi olarak açıkça yazılabilir — nitekim önceki ölçüm çekirdeğindeki iki_kat gibi bir
işlev -> i32 yazarken, gövdesi yalnız bir yan etki üreten bir işlev hiçbir dönüş tipi
yazmaz, çünkü varsayılan dönüş tipi zaten ()tir.
f64 bu altı tip arasında kendine özgü bir sınır taşıyor: taşıdığı değer sayısı sonlu olsa
da, gerçek sayılar sonsuz olduğu için her ondalık değer tam karşılığını bulamıyor — 2.5
gibi bazı değerler tam temsil edilirken, bazı basit kesirler yaklaşık bir değerle temsil
ediliyor. Bu ders bu yaklaşıklığı ayrıca ölçmüyor; burada yalnız kayanın kendisinin bir
ifade olarak nasıl bir değer ürettiği gösterildi, hassasiyetin sınırları kendi başına ayrı
bir konudur.
Diğer beş tipin her biri birden çok değer alabiliyor — bir i32 milyarlarca farklı değerden
biri olabilir, bir bool en azından ikisinden. () bu düzenden ayrılıyor: alabileceği değer
sayısı tam olarak bir. Bu, ()i diğer beş tipten “daha az” yapmıyor, yalnız farklı
yapıyor — bir tipin taşıdığı bilgi miktarı, o tipin kaç ayrı değer alabileceğiyle ölçülürse,
() sıfır bilgi taşıyor demektir ve bu da tam olarak bir deyimin iletmek istediği şeydir:
“bu satır çalıştı” bilgisinin kendisi, hangi değerin üretildiğinden bağımsız.
Altı tipi taşıdıkları değer sayısına göre bir sıraya koyarsak, () bir uçta durur (bir
değer), bool onun hemen yanında (iki değer), sonra char (yaklaşık bir milyon
Unicode kod noktası kadar), sonra i32 ve f64 (milyarlarca değer) gelir; demedin kendi
değer sayısı, bileşenlerinin değer sayılarının çarpımıdır. Bu sıralama rastgele değil: bir
tipin taşıyabileceği bilgi miktarı doğrudan bu sayıyla ilişkilidir ve ()in en uçta
durması, bir deyimin neden “bilgi taşımayan bir sonuç” olarak tanımlandığını gösteriyor.
Bu okuma, ()in neden booldan farklı olduğunu da açıklıyor. bool iki değer taşıdığı için
bir “evet/hayır” bilgisini kodlayabiliyor; () tek değer taşıdığı için hiçbir ayrım
kodlayamıyor — üretildiğini bilmek, hangi seçeneğin gerçekleştiğini bilmekle aynı şey değil,
çünkü seçenek tek. Bir yöntemin dönüş tipi () ise, o yöntemin çağrısının başarılı
olduğunu bilirsin (aksi hâlde program çökerdi ya da derleyici reddederdi), ama çağrının
hangi durumu ürettiğini bilemezsin — bu, önceki dersteki Vec::push örneğiyle aynı
noktadır: push bir eleman eklendiğini garanti eder, ama eklemenin “nasıl” gittiğine dair
hiçbir ek bilgi vermez.
Tam Sayı Taşması: Tanımlı Bir Davranış
Tam sayı tipleri sabit bir bit genişliğine sahiptir ve bu genişliği aşan bir toplam
taşar (overflow). Rust bu durumu tanımsız bırakmaz; taşmanın ne olacağını açıkça
seçmeni ister. Sarma (wrapping_add) yöntemi bu seçimlerden biridir: sonucu, tipin
alabileceği en büyük değerin bir fazlasını en küçük değere döndürerek hesaplar.
// tasma_ve_karakter.rs — tasmanin tanimli davranisi ve karakterin bayt olmamasi
fn main() {
let en_buyuk: u8 = u8::MAX;
let tasmis = en_buyuk.wrapping_add(1);
println!("en buyuk u8: {}", en_buyuk);
println!("tasma sonrasi: {}", tasmis);
let ascii_kelime = "agac";
let ozel_kelime = "ağaç";
println!("agac bayt: {} karakter: {}", ascii_kelime.len(), ascii_kelime.chars().count());
println!("ağaç bayt: {} karakter: {}", ozel_kelime.len(), ozel_kelime.chars().count());
}
en buyuk u8: 255 tasma sonrasi: 0 agac bayt: 4 karakter: 4 ağaç bayt: 6 karakter: 4
u8 sekiz bitlik bir tam sayı tipidir ve alabileceği en büyük değer 255tir. 255e 1
eklemek normal toplamada tipin sınırını aşar; wrapping_add bu aşımı tanımlı bir kurala
bağlıyor — sonucu tipin aralığına “sarıyor” ve 0a dönüyor. Bu, taşmanın rastgele bir
davranışa yol açmadığını gösteriyor: wrapping_add her koşumda, her makinede aynı yanıtı
verir, çünkü kural bit genişliğinin kendisinden geliyor, çalışma zamanının o anki durumundan
değil.
Bu davranış rastgele seçilmiş bir örnek değil, gerçek bir ihtiyacın karşılığıdır: döngüsel
olarak sayılıp belirli bir sınırda başa dönmesi beklenen bir değer — bir hafta içindeki gün
sırası, dairesel bir arabellekteki konum — tam olarak sarma davranışına ihtiyaç duyar.
wrapping_add bu ihtiyacı tek bir çağrıyla karşılıyor ve sonuç, taşmanın hangi kurala
göre ele alınacağını kaynağın kendisinde görünür kılıyor. Standart kitaplık aynı ailede başka
yöntemler de sunuyor — taşmayı bir hata olarak bildiren ve taşmayı tipin sınırında durduran
yöntemler gibi; her biri aynı soruna farklı bir tanımlı yanıt veriyor, ama hiçbiri tanımsız
bırakmıyor.
RD12. Bir tam sayı tipinin bit genişliği (u8 için sekiz bit, i32 için otuz iki bit)
dilin kendisinde sabittir ve makineye göre değişmez — bu, işaretçi genişliğine bağlı olan
usize gibi tiplerden farklıdır. wrapping_addin her koşumda aynı sonucu vermesinin nedeni
tam olarak budur: ölçülen şey donanıma değil, tipin kendi tanımına ait bir sınır.
Karakter Bir Bayt Değildir
Aynı kaynağın ikinci yarısı iki kelimenin uzunluğunu iki farklı biçimde ölçüyor. len()
dizginin bayt cinsinden uzunluğunu veriyor, chars().count() ise dizginin kaç
karakter içerdiğini sayıyor. "agac" dizgisinde bu ikisi eşit çıkıyor — dört bayt, dört
karakter — ama "ağaç" dizgisinde ayrışıyor: altı bayt, dört karakter.
Bunun nedeni Rust’ın dizgileri UTF-8 ile kodlamasıdır — İngilizce alfabesindeki harfler tek
bir baytla temsil edilirken, ğ ve ç gibi harfler iki bayt gerektiriyor. char tipi
kendisi tek bir Unicode kod noktasını temsil ediyor, dizgideki bir baytı değil; bu yüzden
bir char değeri her zaman bir karakterdir, ama bir bayt her zaman bir karakter
değildir. Dört harfli "ağaç" kelimesinin altı bayt tutması, bu ayrımın somut
sonucudur: kelimeyi bayt bayt okumaya çalışan bir kod, ortadaki iki harfi yanlış yerden
bölerdi; chars() bu bölünmeyi engelleyip her seferinde tam bir kod noktası veriyor. İki
kelimenin yan yana konması, farkın yalnız Türkçeye özgü harflerde ortaya çıktığını da
gösteriyor — salt ASCII karakterlerden oluşan bir dizgide bayt sayısı ile karakter sayısı
zaten örtüşüyor, çünkü ASCII’nin her karakteri tek bir baytla temsil ediliyor.
Bu ayrımın kaynak üzerindeki karşılığı şu: bir dizginin uzunluğunu okurken hangi sorunun sorulduğuna dikkat etmek gerekiyor. “Kaç bayt tutuyor” sorusunun yanıtı depolama ve ağ üzerinden taşıma için önemlidir; “kaç karakter içeriyor” sorusunun yanıtı ise kullanıcıya gösterilecek uzunluk için önemlidir. İkisi aynı dizgi için farklı sayılar verebildiğinden, bir kod tabanının hangi soruyu sorduğunu bilmemesi, ölçülemeyen bir hata sınıfına dönüşebilir.
Bu ders boyunca ölçülen altı tip (beş temel tip ve ()) hep aynı iki soruyla karşılandı:
bu tipin ürettiği değer kümesi nedir ve bu kümenin sınırı nereden geliyor. Tam sayı ve
karakter için sınır bit genişliğinden geldi; demet için sınır bileşen sayısından; () için
sınır tipin tanımından — tek bir değer. Bu sorular önceki iki dersteki sınırlayıcı
ölçümlerle aynı yöntemi izliyor: bir garantinin kapsamını, garantiyi veren yapının kendi
tanımına bakarak çizmek. Sıradaki derste bu altı tip artık hazır malzeme olarak kullanılacak
ve odak, bu malzemelerin bir gövde içinde nasıl bir araya getirilip tek bir dönüş değerine
dönüştüğüne kayacak.
Özet
- Beş temel tip (tam sayı, kayan noktalı, mantıksal, karakter, demet) birer ifade olarak
koşturulduğunda kendi tipinde bir değer üretir; demedin bileşenleri hem indeksle (
.0) hem yapısökümle (let (a, b) = ...) okunabilir. ()gerçek bir tiptir ve tek bir değeri vardır; bir deyimin ürettiği şey yokluk değil, tam olarak bu tipin o tek değeridir.- Tam sayı taşması Rust’ta tanımsız değildir;
wrapping_addgibi yöntemler taşmanın nasıl ele alınacağını açıkça belirler ve sonuç her koşumda aynıdır. - Sabit bit genişlikli tam sayı tipleri (
u8,i32gibi) makineye göre değişmez; bu, işaretçi genişliğine bağlı tiplerden ayrı bir sınıftır. - Bir
charbir Unicode kod noktasıdır, bir bayt değildir; bir dizginin bayt uzunluğu ile karakter sayısı farklı sayılar olabilir. - Bir demedin tipi bileşenlerin hem tipleriyle hem sayısıyla belirlenir; sayı derleme zamanında sabittir ve yapısöküm deseninin sağdaki demetle birebir eşleşmesini zorunlu kılar.
Sonraki Adım
Değerlerin tipleri artık tanımlı, ama bu değerleri üreten yazımların çoğu şimdiye kadar tek
satırlık ifadelerdi. Sıradaki ders bir gövdenin tamamının nasıl bir değer ürettiğini
sorar — bir fonksiyonun son satırının noktalı virgülle bitip bitmemesinin dönüş değerini
nasıl belirlediğini ve bu satırın ne zaman return yazmayı gerektirdiğini koşturarak
gösterir. () orada da karşımıza çıkacak: gövdesi yalnız bir yan etki üreten bir işlevin
dönüş tipi neden hiç yazılmadan da geçerli olduğu, bu dersin kurduğu tanımın doğrudan bir
sonucu olarak görülecek.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.