ch05-02-example-structs
Bir Struct Kullanarak Örnek Program
Struct'ları ne zaman kullanmamız gerektiğini anlamak için, bir dikdörtgenin alanını hesaplayan bir program yazalım. Tekil değişkenler kullanarak başlayacağız ve ardından programı struct'lar kullanacak şekilde yeniden yapılandıracağız.
Piksellerle belirtilen bir dikdörtgenin genişliğini ve yüksekliğini alarak alanını hesaplayacak rectangles adında yeni bir ikili proje oluşturalım. Aşağıdaki 5-8 numaralı liste, projemizde böyle bir hesaplama yapmanın kısa bir programını gösteriyor: src/main.rs.
{{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/listing-05-08/src/main.rs:all}}
Şimdi, bu programı cargo run
komutuyla çalıştırın:
{{#include ../listings/ch05-using-structs-to-structure-related-data/listing-05-08/output.txt}}
Bu kod, her boyutla area
fonksiyonunu çağırarak dikdörtgenin alanını bulma konusunda başarılı, ancak bu kodu daha net ve okunabilir hale getirmek için daha fazlasını yapabiliriz.
Bu kodun sorunu, area
fonksiyonunun imzasında açıktır.
{{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/listing-05-08/src/main.rs:here}}
area
fonksiyonu bir dikdörtgenin alanını hesaplamak için tasarlanmıştır, ancak yazdığımız fonksiyonun iki parametresi var ve programımızda bu parametrelerin ilişkili olduğu hiç bir şey net değil. Genişlik ve yüksekliği bir arada gruplamak daha okunabilir ve daha yönetilebilir olacaktır. Bunu, 3. Bölümdeki “Tuple Tipi” bölümünde tartıştığımız bir şekilde yapabiliriz: tuple'lar kullanarak.
Tuple'lar ile Yeniden Yapılandırma
Aşağıdaki 5-9 numaralı liste, tuple'lar kullanan programımızın bir başka versiyonunu göstermektedir.
{{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/listing-05-09/src/main.rs}}
Bir açıdan, bu program daha iyidir. Tuple'lar biraz yapı eklememizi sağlar ve şimdi sadece bir argüman geçiriyoruz. Ancak başka bir açıdan, bu versiyon daha az net: tuple'lar elemanlarını adlandırmaz, bu yüzden tuple'ın parçalarına indeksle erişmemiz gerekir ve bu da hesaplamamızı daha az belirgin hale getirir.
Önemli Nokta: Genişlik ve yüksekliği karıştırmak alan hesabı için önemli olmayacak, ancak eğer dikdörtgeni ekrana çizeceksek bu önemli olacaktır!
width
'in tuple indeksi0
veheight
'in tuple indeksi1
olduğunu akılda tutmalıyız. Başka birinin kodumuzu kullanması durumunda bu, anlaması daha zor olacaktır. Kodumuzda verimizin anlamını iletmediğimiz için hata yapma olasılığı artar.
Struct'lar ile Yeniden Yapılandırma: Daha Fazla Anlam Eklemek
Veriyi etiketleyerek anlam eklemek için struct'ları kullanırız. Kullandığımız tuple'ı, tümüne bir isim ve parçalara isimler vererek bir struct'a dönüştürebiliriz; bu, 5-10 numaralı listede gösterilmiştir.
{{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/listing-05-10/src/main.rs}}
Burada Rectangle
adında bir struct tanımladık. Süslü parantezlerin içinde, width
ve height
olarak iki alan tanımladık ve her ikisi de u32
türünde. Ardından, main
içinde genişliği 30
ve yüksekliği 50
olan belirli bir Rectangle
örneği oluşturduk.
area
fonksiyonumuz artık bir parametre ile tanımlanmıştır, bu parametreye rectangle
adını verdik ve tipi bir Rectangle
örneğinin değiştirilemez bir borcudur. 4. Bölümde bahsettiğimiz gibi, struct'ın mülkiyetini almak yerine onu ödünç almak istiyoruz. Bu sayede, main
mülkiyetini korur ve rect1
üzerinde kullanmaya devam edebiliriz; bu yüzden fonksiyon imzasında &
kullandık.
area
fonksiyonu, Rectangle
örneğinin width
ve height
alanlarına erişir (bir borçlu struct örneğinin alanlarına erişmenin alan değerlerini taşımaz; bu nedenle, genellikle struct'ların borçlarını görürsünüz). Artık area
fonksiyonu tam olarak ne anlama geldiğini söylüyor: Rectangle
'ın alanını hesaplamak, width
ve height
alanlarını kullanarak. Bu, genişlik ve yüksekliğin birbiriyle ilişkili olduğunu iletir ve değerlerin adına, 0
ve 1
gibi tuple indeks değerleri yerine tanımlayıcı isimler verir. Bu netlik açısından bir kazanım.
Türetilmiş Niteliklerle Kullanışlı İşlevsellik Eklemek
Programımızı debug ederken bir Rectangle
örneğini yazdırmanın ve tüm alanlarının değerlerini görmenin yararlı olacağını düşündük. Aşağıdaki 5-11 numaralı liste, daha önceki bölümlerde kullandığımız println!
makrosunu denemektedir. Ancak bu çalışmayacak.
{{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/listing-05-11/src/main.rs}}
Bu kodu derlediğimizde, şu ana mesajı veren bir hata alıyoruz:
{{#include ../listings/ch05-using-structs-to-structure-related-data/listing-05-11/output.txt:3}}
println!
makrosu birçok tür formatlama yapabilir ve varsayılan olarak, süslü parantezler println!
'e Display
adı verilen bir formatlama kullanmasını söyler: doğrudan son kullanıcıya hitap eden çıktılar için tasarlanmıştır. Şimdiye kadar gördüğümüz ilkel türler varsayılan olarak Display
'i uygular çünkü bir 1
veya başka herhangi bir ilkel türü kullanıcıya göstermek için tek bir yol vardır. Ancak struct'larla, println!
'in çıktıyı nasıl formatlaması gerektiği daha az nettir çünkü daha fazla görüntüleme olasılığı vardır: Virgül ister misiniz? Süslü parantezleri yazmak ister misiniz? Tüm alanları göstermeli mi? Bu belirsizlik nedeniyle Rust, ne istediğimizi tahmin etmeye çalışmaz ve struct'ların println!
ile kullanabileceği Display
uygulaması yoktur.
Hataları okumaya devam edersek, bu yararlı notu buluruz:
{{#include ../listings/ch05-using-structs-to-structure-related-data/listing-05-11/output.txt:9:10}}
Haydi deneyelim! println!
makrosu çağrısını şimdi println!("rect1 is {rect1:?}");
şeklinde yapacağız. Süslü parantezlerin içinde :?
belirteci, println!
'e Debug
adı verilen bir çıktı formatını kullanmak istediğimizi söyler. Debug
niteliği, yapıların değerini debug sırasında görmek için yararlı bir şekilde yazdırmamıza olanak tanır.
Bu değişiklikle kodu derleyelim. Aman Tanrım! Hala hata alıyoruz:
{{#include ../listings/ch05-using-structs-to-structure-related-data/output-only-01-debug/output.txt:3}}
Ama yine, derleyici bize yararlı bir not veriyor:
{{#include ../listings/ch05-using-structs-to-structure-related-data/output-only-01-debug/output.txt:9:10}}
Rust debugging bilgilerini yazdırma işlevselliğini sağlar, ancak bu işlevselliği struct'ımız için mevcut hale getirmek için açıkça bunu kabul etmemiz gerekir. Bunu yapmak için, struct tanımının hemen önüne #[derive(Debug)]
dış niteliğini ekliyoruz; bu, 5-12 numaralı listede gösterilmiştir.
{{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/listing-05-12/src/main.rs}}
Artık programı çalıştırdığımızda hiçbir hata almayacağız ve şu çıktıyı göreceğiz:
{{#include ../listings/ch05-using-structs-to-structure-related-data/listing-05-12/output.txt}}
Güzel! En güzel çıktı değil, ancak bu örneğin tüm alanlarının değerlerini gösteriyor; bu da debug süresince kesinlikle yardımcı olacaktır. Daha büyük struct'lar için, biraz daha kolay okunur bir çıktı almak yararlıdır; bu durumlarda, println!
dizesinde {:?}
yerine {:#?}
kullanabiliriz. Bu örnekte, {:#?}
stilini kullanmak şu çıktıyı verecektir:
{{#include ../listings/ch05-using-structs-to-structure-related-data/output-only-02-pretty-debug/output.txt}}
Debug
formatını kullanarak bir değeri yazdırmanın bir başka yolu, dbg!
makrosunu kullanmaktır. Bu makro, bir ifadenin sahipliğini alır (bu, println!
'ın bir referans alması durumunda) ve o ifadenin bulunduğu dosya ve satır numarasını yazdırır ve ifadenin sonuç değerini gösterir, ardından bu değerin sahipliğini geri döner.
dbg!
makrosunu çağırmak, standart hata konsol akışına (stderr
) yazdırır; buna karşılık println!
, standart çıktı konsol akışına (stdout
) yazdırır. stderr
ve stdout
hakkında daha fazla bilgi için 12. Bölümdeki “Standart Çıktı Yerine Standart Hata Akışına Hata Mesajları Yazma” bölümüne göz atacağız.
width
alanına atanan değere ve rect1
'deki yapının değerine ilgi duyduğumuz bir örnek:
{{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/no-listing-05-dbg-macro/src/main.rs}}
dbg!
makrosunu 30 * scale
ifadesinin etrafına koyabiliriz ve çünkü dbg!
ifadenin değerinin sahipliğini döndürür, width
alanı dbg!
çağrısının orada olmadığı gibi aynı değeri alacaktır. dbg!
'ın rect1
'in sahipliğini almasını istemiyoruz, bu yüzden bir sonraki çağrıda rect1
'in referansını kullanıyoruz. İşte bu örneğin çıktısı:
{{#include ../listings/ch05-using-structs-to-structure-related-data/no-listing-05-dbg-macro/output.txt}}
Çıktının ilk kısmı, 30 * scale
ifadesini debug ettiğimiz src/main.rs dosyasının 10. satırından geldi ve sonucu 60
olarak çıktı (tam sayıların Debug
formatı, yalnızca değerlerini yazdırmak için uygulanır). src/main.rs
dosyasının 14. satırındaki dbg!
çağrısı ise Rectangle
yapısının değerini gösterir. Bu çıktı, Rectangle
türünün güzel Debug
formatını kullanır. dbg!
makrosu, kodunuzun ne yaptığını anlamaya çalışırken gerçekten yararlı olabilir!
Debug
niteliğine ek olarak, Rust, özel türlerimize yararlı davranış ekleyebilen bir dizi nitellik verir. Bu nitelikler ve davranışları Ek C'de listelenmiştir. Bu niteliklerin özel davranışlarla nasıl uygulanacağını ve kendi niteliklerimizi nasıl oluşturacağımızı 10. Bölümde ele alacağız. derive
dışında başka birçok nitelik de bulunmaktadır; daha fazla bilgi için Rust Referansındaki “Nitelikler” bölümüne bakın.
area
fonksiyonumuz çok spesifiktir: yalnızca dikdörtgenlerin alanını hesaplar. Bu davranışı Rectangle
struct'ımıza daha sıkı şekilde bağlamak yararlı olacaktır çünkü başka bir tür ile çalışmaz. Bu kodu, area
fonksiyonunu Rectangle
türü üzerinde tanımlanmış bir area
metoduna dönüştürerek nasıl yeniden yapılandırabileceğimize bakalım.