İçeriğe geç
academia.sh

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_add gibi 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, i32 gibi) makineye göre değişmez; bu, işaretçi genişliğine bağlı tiplerden ayrı bir sınıftır.
  • Bir char bir 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.

Aramak için yazmaya başlayın.

↑↓ ↵ Esc gezin · aç · kapat