Ana içeriğe geç

ch03-02-data-types

Veri Türleri

Rust'taki her değer belirli bir veri türü içerisindedir; bu, Rust'a hangi tür verinin tanımlandığını bildirir, böylece bu verilerle nasıl çalışacağını biliriz. İki veri türü alt kümesine bakacağız: skalar ve bileşik.

not

Unutmayın ki Rust, statik olarak türlendirilmiş bir dildir; bu, tüm değişkenlerin türlerini derleme zamanında bilmesi gerektiği anlamına gelir.

Derleyici genellikle hangi türü kullanmak istediğimizi, değer ve kullanım şeklimize dayanarak çıkarabilir. Birden fazla türün mümkün olduğu durumlarda, örneğin “Tahmini Gizli Sayı ile Karşılaştırma” bölümünde, bir String'i sayısal bir türe parse ile dönüştürdüğümüzde, bir tür açıklaması eklememiz gerekir; örneğin:

let guess: u32 = "42".parse().expect("Bir sayı değil!");

Önceki kodda gösterilen : u32 tür açıklamasını eklemezsek, Rust aşağıdaki hatayı gösterir; bu, derleyicinin hangi türü kullanmak istediğimizi bilmek için bizden daha fazla bilgiye ihtiyaç duyduğu anlamına gelir:

{{#include ../listings/ch03-common-programming-concepts/output-only-01-no-type-annotations/output.txt}}

Diğer veri türleri için farklı tür açıklamaları göreceksiniz.


Skalar Türler

Skalar tür, tek bir değeri temsil eder. Rust'ın dört temel skalar türü vardır: tam sayılar, ondalık sayılar, Boolean'lar ve karakterler. Bunları diğer programlama dillerinden tanıyor olabilirsiniz. Şimdi Rust'taki nasıl çalıştıklarına bakalım.

Tam Sayı Türleri

Bir tam sayı, kesirli bir bileşeni olmayan bir sayıdır. Bölüm 2'de bir tam sayı türü kullandık; bu u32 türüdür. Bu tür bildirim, ilişkili olduğu değerin işaretsiz bir tam sayı (işaretli tam sayı türleri u yerine i ile başlar) olması gerektiğini ve 32 bit alan kapladığını gösterir. Tablo 3-1, Rust'taki yerleşik tam sayı türlerini göstermektedir. Bir tam sayı değerinin türünü bildirmek için bu varyantların herhangi birini kullanabiliriz.

Tablo 3-1: Rust'taki Tam Sayı Türleri

Uzunlukİşaretliİşaretsiz
8-biti8u8
16-biti16u16
32-biti32u32
64-biti64u64
128-biti128u128
mimariisizeusize
bilgi

Her varyant ya işaretli ya da işaretsiz olabilir ve belirli bir boyuta sahiptir. İşaretli ve işaretsiz, sayının negatif olmasının mümkün olup olmadığını belirtir.

Bu, sayıları kağıda yazmaya benzer: imzanın önemli olduğu durumlarda, bir sayı artı (+) veya eksi (-) işareti ile gösterilir; ancak sayının pozitif olduğunu varsaymanın güvenli olduğu durumlarda, işaretsiz gösterilir. İşaretli sayılar iki'nin tamamı temsili kullanılarak saklanır.

Her işaretli varyant, -(2n - 1) ile 2n - 1 - 1 arasındaki sayıları saklayabilir; burada n, bu varyantın kullandığı bit sayısını belirtir. Dolayısıyla bir i8, -(27) ile 27 - 1 arasında değer saklayabilir; bu da -128 ile 127 arasında anlamına gelir. İşaretsiz varyantlar, 0 ile 2n - 1 arasında sayıları saklayabilir; dolayısıyla bir u8, 0 ile 28 - 1 arasında yani 0 ile 255 arasında değer saklayabilir.

Ayrıca, isize ve usize türleri, programınızın çalıştığı bilgisayarın mimarisine bağlıdır; bu tablo da "mimari" olarak belirtilmiştir: 64 bit, 64 bit mimarideysanız ve 32 bit, 32 bit mimarideysanız.


ipucu

Tam sayı sabitlerini Tablo 3-2'de gösterildiği gibi herhangi bir formda yazabilirsiniz. Birden fazla sayısal türü temsil edebilen sayı sabitleri, türü belirtmek için bir tür ekine, örneğin 57u8, izin verir. Sayı sabitleri ayrıca, sayıyı okumayı kolaylaştırmak için _ tırnak işaretini kullanabilir; örneğin 1_000, 1000 olarak belirtmiş olsaydınız aynı değere sahip olacaktır.

Tablo 3-2: Rust'taki Tam Sayı Sabitleri

Sayı sabitleriÖrnek
Ondalık98_222
Altıgen0xff
Sekizgen0o77
İkilik0b1111_0000
Byte (u8 sadece)b'A'
tehlike

Hangi tam sayı türünü kullanmanız gerektiğini nasıl bilirsiniz? Emin değilseniz, Rust'ın varsayılanları genellikle iyi başlangıç noktalarıdır: tam sayı türleri varsayılan olarak i32'dir.

isize veya usize'yi kullanmanız gereken ana durum, bir tür koleksiyonunu dizinlemek olduğunda olur.

Tam Sayı Taşması

Diyelim ki 0 ile 255 arasında değerler tutabilen bir u8 türünde değişkeniniz var. Eğer değişkenin değerini bu aralığın dışındaki bir değere, örneğin 256'ya değiştirmeye çalışırsanız, tam sayı taşması gerçekleşecektir; bu da iki tür davranışa yol açabilir. Hata modunda derleme yaptığınızda, Rust taşma durumunu kontrol eden kontrol mekanizmaları içerir ve programınız bu davranış gerçekleşirse çalıştırma zamanında panic durumu alır. Rust, bir program hata ile çıktığında bu durumu panikleme terimini kullanır; panikleri daha ayrıntılı olarak “Panic! ile Geri Dönülemez Hatalar” bölümünde tartışacağız.

Çıkış modunda --release bayrağı ile derleme yaptığınızda, Rust, paniklere neden olan tam sayı taşmasını kontrol etmez. Bunun yerine taşma durumunda, Rust iki'nin tamamı sarılması gerçekleştirir. Kısacası, türün tutabileceğinden daha büyük değerler, o türün tutabileceği değerlerin en düşük değerine "sarılır". Bir u8 durumunda 256 değeri 0 olur, 257 değeri 1 olur ve bu şekilde devam eder. Program paniklemeyecektir, ancak değişken beklediğiniz değerle muhtemelen aynı olmayacaktır. Tam sayı taşmasından gelen sarılma davranışına güvenmek bir hata olarak kabul edilir.

Taşma olasılığını açıkça ele almak için, standart kütüphane tarafından sağlanan bu yöntem gruplarını kullanabilirsiniz:

  • Taşmanın her modunun sarıldığı wrapping_* yöntemleri, örneğin wrapping_add.
  • Taşma oluşursa None değerini döndüren checked_* yöntemleri.
  • Taşma olup olmadığını belirten bir boolean ile birlikte değeri döndüren overflowing_* yöntemleri.
  • Değerin minimum veya maksimum değerinde doygun hale gelmesini sağlayan saturating_* yöntemleri.

Ondalık Sayı Türleri

Rust ayrıca ondalık sayılar için iki temel türe sahiptir; bunlar ondalık noktası olan sayılardır. Rust'ın ondalık sayı türleri f32 ve f64 olup, sırasıyla 32 bit ve 64 bit büyüklüğündedir. Varsayılan tür f64'tür çünkü modern CPU'larda, f32 ile yaklaşık olarak aynı hızda çalışmasına rağmen daha fazla hassasiyet sunmaktadır. Tüm ondalık sayı türleri işaretlidir.

Aşağıdaki kod, ondalık sayıların kullanımını gösteren bir örnektir:

Dosya Adı: src/main.rs

{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-06-floating-point/src/main.rs}}

Ondalık sayılar IEEE-754 standardına göre temsil edilir. f32, tek hassasiyetli bir float, f64 ise çift hassasiyetli bir float'tur.

Sayısal İşlemler

Rust, tüm sayı türleri için beklediğiniz temel matematiksel işlemleri destekler: toplama, çıkarma, çarpma, bölme ve kalan. Tam sayı bölmesi, sıfıra en yakın tam sayıya yuvarlar. Aşağıdaki kod, bir let ifadesinde her sayısal işlemi nasıl kullanabileceğinizi gösterir:

Dosya Adı: src/main.rs

{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-07-numeric-operations/src/main.rs}}

Bu ifadelerdeki her ifade bir matematiksel operatör kullanır ve tek bir değere hesaplayarak ardından bir değişkene bağlanır. Ek B Rust'ın sunduğu tüm operatörlerin bir listesini içerir.

Boolean Türü

Çoğu diğer programlama dilinde olduğu gibi, Rust'taki bir Boolean türünün iki olası değeri vardır: true ve false. Boolean'lar bir byte boyutundadır. Rust'taki Boolean türü bool ile gösterilir. Örneğin:

Dosya Adı: src/main.rs

{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-08-boolean/src/main.rs}}

Boolean değerleri kullanmanın temel yolu, if ifadesi gibi koşul ifadeleri ile kullanmaktır. Rust'taki if ifadelerinin nasıl çalıştığını “Kontrol Akışı” bölümünde inceleyeceğiz.

Karakter Türü

Rust'ın char türü, dilin en basit alfabetik türüdür. İşte char değerlerini tanımlamanın bazı örnekleri:

Dosya Adı: src/main.rs

{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-09-char/src/main.rs}}

char sabitlerini tek tırnak ile belirttiğimizi, dize sabitlerinin ise çift tırnak kullandığını unutmayın. Rust'ın char türü dört byte boyutundadır ve bir Unicode Scalar Değeri temsil eder; bu, yalnızca ASCII'den daha fazlasını temsil edebileceği anlamına gelir. Aksanlı harfler; Çince, Japonca ve Korece karakterler; emoji; ve sıfır genişlikli boşluklar, Rust'taki geçerli char değerleridir. Unicode Scalar Değerleri, U+0000 ile U+D7FF ve U+E000 ile U+10FFFF arasında değer alır. Ancak, Unicode'da "karakter" gerçekten bir kavram değildir; bu nedenle insan sezginizin bir "karakter"ın ne olduğu ile Rust'taki char arasındaki fark size alışılmadık görünebilir. Bu konuyu “UTF-8 Kodlu Metinleri Dizelerle Saklama” bölümünde ayrıntılı olarak tartışacağız.


Bileşik Türler

Bileşik türler, birden fazla değeri bir türde gruplayabilir. Rust'ta iki temel bileşik tür vardır: demetler ve diziler.

Demet Türü

Bir demet, birden fazla değeri çeşitli türlerde bir arada gruplamanın genel bir yoludur. Demetlerin sabit bir uzunluğu vardır: bir kez tanımlandıklarında boyutu büyüyemez veya küçülemez.

bilgi

Bir demet oluşturmak için, parantez içinde virgülle ayrılmış bir değer listesi yazarız. Demetteki her pozisyonda bir tür vardır ve demetteki farklı değerlerin türleri aynı olmak zorunda değildir.

Bu örnekte isteğe bağlı tür açıklamaları ekledik:

Dosya Adı: src/main.rs

{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-10-tuples/src/main.rs}}

Tup değişkeni tüm demete bağlanır çünkü demet tek bir bileşik eleman olarak kabul edilir. Demet değerinden bireysel değerleri almak için, bir desen eşleşmesi kullanarak demet değerini dağıtabiliriz; şöyle:

Dosya Adı: src/main.rs

{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-11-destructuring-tuples/src/main.rs}}

Bu program önce bir demet oluşturur ve onu tup değişkenine bağlar. Ardından, let ile bir desen kullanarak tup'u alır ve üç ayrı değişken olan x, y ve z'ye dönüştürür. Bu, demeti üç parçaya ayırdığı için dağıtma olarak adlandırılır. Son olarak, program y'nin değerini, yani 6.4 değerini yazdırır.

Bir demet elemanına doğrudan erişmek için, erişmek istediğimiz değerin dizinini takip eden bir nokta (.) kullanabiliriz. Örneğin:

Dosya Adı: src/main.rs

{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-12-tuple-indexing/src/main.rs}}

Bu program x demetini oluşturur ve ardından demetin her elemanına karşılık gelen dizinlerini kullanarak erişir. Çoğu programlama dilinde olduğu gibi, bir demetteki ilk dizin 0'dır.


tehlike

Herhangi bir değeri olmayan demet için özel bir isim vardır: birim. Bu değer ve ona karşılık gelen tür her ikisi de () olarak yazılır ve boş bir değeri veya boş bir dönüş türünü temsil eder. İfadeler, başka bir değer döndürmüyorsa, birim değerini döndürür.

Dizi Türü

Birden fazla değerin bir araya gelmesi için bir diğer yol bir dizi oluşturmaktır. Bir demetin aksine, bir dizinin her elemanı aynı türde olmak zorundadır. Diğer bazı dillerdeki dizilerin aksine, Rust'taki diziler sabit bir uzunluğa sahiptir.

Dizi elemanlarını, köşeli parantezler içinde virgülle ayrılmış bir liste olarak yazarız:

Dosya Adı: src/main.rs

{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-13-arrays/src/main.rs}}

Diziler, verilerinizi yığıt üzerinde, gördüğümüz diğer türler gibi tahsis etmek istediğinizde yararlıdır; yığın ile (bu konuyu Bölüm 4'te daha ayrıntılı olarak tartışacağız) veya daima sabit sayıdaki elemanlara sahip olmayı sağlamak istediğinizde yararlıdır. Ancak, diziler vektör türünden daha esnek değildir. Vektör, standart kütüphane tarafından sağlanan ve büyüyüp küçülmesine izin verilen benzer bir koleksiyon türüdür. Hangi türü kullanmanız gerektiğinden emin değilseniz, muhtemelen bir vektör kullanmalısınız. Bölüm 8'de vektörleri daha ayrıntılı olarak ele alacağız.

not

Ancak, diziler, eleman sayısının değişmeyeceğini bildiğinizde daha kullanışlıdır. Örneğin, programınızda ayların adlarını kullanıyorsanız, muhtemelen bir vektör yerine bir dizi kullanırsınız çünkü her zaman 12 eleman içereceğini bilirsiniz:

let months = ["Ocak", "Şubat", "Mart", "Nisan", "Mayıs", "Haziran", "Temmuz",
"Ağustos", "Eylül", "Ekim", "Kasım", "Aralık"];

Bir dizinin türünü, her elemanın türünü, bir noktalı virgül ve ardından dizinin içindeki eleman sayısını köşeli parantezlerle yazılarak belirtirsiniz:

let a: [i32; 5] = [1, 2, 3, 4, 5];

Burada, i32 her bir elemanın türüdür. Noktalı virgülden sonra gelen 5, dizinin beş eleman içerdiğini gösterir.

Ayrıca, diziye her eleman için aynı değeri içermek için başlangıç değerini belirtip, arkasından bir noktalı virgül ve ardından dizinin uzunluğunu köşeli parantez içerisinde belirterek başlatabilirsiniz; örneğin:

let a = [3; 5];

A adındaki dizi, başlangıçta 3 değerine ayarlanmış 5 eleman içerecektir. Bu, let a = [3, 3, 3, 3, 3]; yazmak yerine daha özlü bir yoldur.


Dizi Elemanlarına Erişim

Bir dizi, önceden bilinen ve sabit boyutta bir bellek parçasıdır ve yığıt üzerinde tahsis edilebilir. Elemanlara erişmek için dizileme kullanarak erişebilirsiniz; şu şekilde:

Dosya Adı: src/main.rs

{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-14-array-indexing/src/main.rs}}

Bu örnekte, first adındaki değişken 1 değerini alır, çünkü bu dizi içindeki [0] dizinindeki değerdir. second adındaki değişken ise dizinin [1] dizininden 2 değerini alır.

Geçersiz Dizi Elemanı Erişimi

Bir dizinin sonundan öte bir elemanı erişmek isterseniz ne olur? Diyelim ki, bölüm 2'deki tahmin oyunu benzeri bir kod yazıyorsunuz ve kullanıcıdan bir dizi indeksi alıyorsunuz:

Dosya Adı: src/main.rs

{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-15-invalid-array-access/src/main.rs}}

Bu kod başarıyla derlenir. Bu kodu cargo run kullanarak çalıştırdığınızda ve 0, 1, 2, 3 veya 4 girerseniz, program o dizindeki ilgili değeri yazdırır. Eğer array'in sonundan geçecek bir sayı, örneğin 10 girerseniz, aşağıdaki gibi çıktılar alırsınız:

thread 'main' panicked at src/main.rs:19:19:
index out of bounds: the len is 5 but the index is 10
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

Program, geçersiz bir değerin dizilmesi sırasında bir çalışma zamanı hatası aldı. Program bir hata mesajı ile çıktı ve son println! ifadesini yürütmedi. Dizileme kullanarak bir eleman erişmeye çalıştığınızda, Rust belirtilen dizinin dizinin uzunluğundan küçük olduğundan emin olacaktır. Bu koşul, dizinin uzunluğuna eşit ya da daha büyükse, Rust panik yapacaktır. Bu kontrol, çalışma zamanında yapılmalıdır; özellikle bu durumda, derleyici kodu çalıştıran kullanıcının hangi değeri gireceğini bilemez.

tehlike

Bu, Rust’ın bellek güvenliği ilkelerinin bir örneğidir. Birçok düşük seviyeli dillerde, bu tür bir kontrol yapılmaz ve geçersiz bir dizin verildiğinde geçersiz belleğe erişilebilir. Rust, bellek erişim izin vermek ve devam etme yerine hemen çıkış yaparak bu tür hatalardan korunmanızı sağlar. Bölüm 9, Rust’ın hata yönetimi ile ilgili daha fazla bilgi ve okuması okunabilir, güvenli kod yazma yöntemlerini tartışmaktadır.