Skip to main content

Rust'ın Temelleri

Bu bölümde bir Rust programının temel konularına giriş yapacağız. Bunlar bütün programlama dillerinde olan ya da birbirlerine benzeyen temel kavramlardan bazılarını içermektedir.

Hello World

Cargo ile oluşturduğumuz bir projede varsayılan olarak ekrana "Hello World!" yazdırılan küçük bir programla başlanır. "src" dosyasının içerisindeki main.rs adlı dosyayı açtığımızda aşağıdaki kod söz dizimini görürüz:

fn main() {
println!("Hello, world!");
}

Bu kod satırlarında "main" adında bir fonksiyon tanımlıdır. main özel bir fonksiyondur, bütün Rust programları bu fonksiyonla başlar. fn anahtar sözcüğü ile bir fonksiyon tanımlanacağı belirtilir ve ardından bu fonksiyonun adı yazılır. Fonksiyon adından sonra parantez açılarak fonksiyonun parametreleri girilir, parametre almayacaksa boş bırakılır. Fonksiyonun içerdiği kodlar için bir süslü parantez açılır, buna fonksiyon gövdesi denir.

main fonksiyonunun gövdesinde içerdiği metni ekrana yazdıran bir kod satırı bulunur. Rust'ta ekrana metin vb bir şey yazdırmak için println!() makrosu kullanılır.

Rust'ta bir kod satırı ifadesi (expression) mutlaka noktalı virgül (;) ile sonlandırılmalıdır. Noktalı virgül ile sonlandırılmayan durumlar da mevcuttur, bunları ilerleyen konularda yeri geldiğinde belirteceğiz.

Rust Projesini Derlemek ve Çalıştırmak

Bir Rust projesini çalıştırmadan önce onu derlemeniz (compile) gerekir. Derleme işlemi yazdığınız kodların makine diline çevrilerek bilgisayarın çalıştırabileceği formata getirilmesidir. Bunun için cargo build komutu kullanılarak derleme işlemi yapılır, derleme bittikten sonra dosya ağacında target adlı bir klasör ve Cargo.lock adlı bir dosya belirir.

target klasörü içerisinde programınızın derlendikten sonraki çalıştırılabilir (executable) dosyası bulunur. Cargo lock dosyası içine ise programınızın bağımlılıkları (dependencies) hakkında bir takım bilgiler otomatik olarak kaydedilir.

cargo build komutu ile programınız derlenir ancak çalıştırılmaz, çalıştırmak için cargo run komutu ile programınızı çalıştırabilirsiniz. Eğer en başta cargo build demeden sadece cargo run komutunu çalıştırırsanız programınız önce otomatik olarak derleme işlemini yapar sonra çalıştırılır.

Veriler ve Veri tipleri

Rust programlama dilinde her değerin bir tipi vardır ve verilere tip ataması statik olarak yapılır. Yani Rust, derleme zamanında bütün verilerin tipini atanmış olarak bilmek ister. Eğer verilerin tipi manuel olarak atanmamışsa kendisi bir çıkarım yaparak varsayılan tip atamasını otomatik olarak yapar. Verinin tipini çıkaramadığı durumlarda ise uyarı vererek mutlaka tip atamasının yapılmasını ister.

Değişken Atamak

fn main() {
// değişken atamak için let anahtar sözcüğünü kullanırız
let x = 1;
}

Yukarıdaki kod blokunda ilk satırda // ile başlayan kısım yorum satırıdır ve derleyici tarafından görmezden gelinir. Yorum satırlarının amacı kod okunurluğunu ve anlaşılırlığını arttırmaktır.

Rust'ta değişken tanımlamak için let anahtar sözcüğü kullanılır. Bu bütün veri tipleri için geçerlidir. Yukarıdaki kod blokunda let anahtar sözcüğü kullanılarak "x" adlı bir değişken tanımlanmış ve değeri 1'e eşittir.

Rust'ta her değişkenin bir tipi vardır ve atanmak zorundadır. Eğer değişkenin tipini atamazsanız Rust arka planda varsayılan olarak tanımlanan tip atamasını yapar. Örneğin yukarıdaki "x" değişkenini değeri bir integer yani tam sayıdır ve biz tip atamasını yapmadığımız için Rust arka planda integer için varsayılan olan "i32" tipini atar.

Manuel olarak tip ataması ise şu şekilde yapılır:

let x: i32 = 1;

Verilerin Değiştirilmezliği

Rust'ta değişkenler ilk atandığında varsayılan olarak "immutable" yani değiştirilemez olarak tanımlanırlar. Aşağıdaki kodu derlediğinizde hata verecektir:

fn main() {
let x: i32 = 1;
x = 5;
}

"x" değişkeninin değerini 5 yapmaya çalıştığımızda hata alırız çünkü varsayılan olarak x değişkeni değiştirilemez. Rust'ta bir değişkeni değiştirebilmek için mut anahtar sözcüğü kullanılmalıdır.

fn main() {
let mut x: i32 = 1; // x'in değeri 1'e eşit
x = 5; // artık x'in değeri 5'e eşit
}

Sabit değerler

Sabit değerler (constants) "mut" anahtar sözcüğünün kullanılamadığı ve hiçbir zaman değiştirilmeyen değerlerdir. let yerine const anahtar sözcüğü alarak atanırlar ve tip ataması mutlaka yapılmalıdır. Sabit değerlerin adları her zaman büyük harflerle yazılır.

const MYVAL: i32 = 61;

Skaler Veri tipleri

Skaler tipler tek bir değere eşit olan veri tipleridir, yani birden fazla değişkeni tutmak için kullanılmazlar. Rust'ta dört farklı skaler veri tipi vardır: tam sayılar (integers), kayan noktalı sayılar (floating point numbers), mantıksallar (booleans) ve karakterler (characters).

Tam sayılar

Tam sayılar adından da anlaşılacağı üzere kesirli bileşeni olmayan sayılardır. Doğal sayılar ve onların negatiflerinden oluşurlar. Rust'ta kapladığı boyuta göre birden fazla tam sayı tipi bulunur.

Boyutİşaretliİşaretsiz
8-biti8u8
16-biti16u16
32-biti32u32
64-biti64u64
128-biti128u128

Yukarıdaki tabloda Rust'ta bir tam sayının alabileceği tipler listelenmektedir. İşaretli tipler negatif sayı da alabilirken işaretsiz tipler sadece pozitif sayılardan oluşur. Her tam sayı değerinin boyutu atandığı tipe göre değişir. Örneğin sayının tipi i16 ise bellekte 16 bit yer kaplar, Her tip için belli aralıklarda sayılar tutulabilir, bunun için bir formül kullanılarak o tipin o sayıyı saklayıp saklayamayacağı anlaşılabilir:

n bit sayısını temsil etmek üzere işaretli sayılar için -(2^(n - 1)) den 2^(n - 1) - 1 e kadar işaretsiz sayılar için 0 dan 2^n - 1 e kadar olan aralıktaki sayılar o tamsayı tipi için o değeri alabilir.

Örneğin "i8" tipinde bir tam sayı n=8 olmak üzere formülde yerleştirip işlem yapıldığında -128 ile 127 aralığında bütün sayıları tutabilir. Eğer bu sayı aralığında olmayan örneğin 211 sayısı i8 tipinde tutulmak istenirse aralık dışında olacağından atanamaz.

Tablodakiler dışında tam sayılar için atanabilecek iki tip daha vardır, bunlar isize ve usize olmak üzere iki tanedir. Bu iki tip atandığında bilgisayarın işlemci mimarisi kaç bitse otomatik olarak onu atat, örneğin tam sayının tipi "isize" olarak belirlenmişse ve bilgisayar 32 bitlikse i32 olarak, 64 bitlikse i64 olarak tip ataması yapılır.

Kayan Noktalı Sayılar

Rust iki farklı kayan noktalı sayı tipine sahiptir, bunlar f32ve f64 olmak üzere 32 bitlik ve 64 bitlik olarak tanımlanırlar. Eğer tip ataması manuel olarak yapılmazsa Rust derleyicisi varsayılan olarak f64 atayacaktır. Kayan noktalı sayıların hepsi işaretli olarak tanımlanmıştır, yani negatif ve pozitif değerler alabilirler.

fn main() {
let x = 3.0; // tip ataması yapılmadığından varsayılan olarak f64 atanır.

let y: f32 = 6.4;
}

Sayısal İşlemler

Rust'ta toplama, çıkarma, çarpma ve bölme gibi temel matematiksel işlemler diğer programlama dillerindekine benzer şekilde yapılır. Tam sayıların bölümünde çıkan sonuç sıfıra en yakın olan tam sayıya yuvarlanır. Örneğin 2.8 sayısı 2 ye yuvarlanır.

fn main() {
// toplama
let toplam = 8 + 5;

// çıkarma
let fark = 23.6 - 8.3;

// çarpma
let carp = 4 * 30;

// bölme
let bolum = 64.7 / 30.4;
let x = -5 / 3; // Sonuç -1 e yuvarlanır.
let y = 14 / 5; // Sonuç 2 ye yuvarlanır.

// bölümden kalanı bulma
let kalan = 43 % 5;
}

Mantıksal (Boolean)

Diğer çoğu dilde olduğu gibi Rust'ta da mantıksal (boolean) tipte iki değer bulunur: true ve false. bool yazılarak tip ataması yapılır ve bellekte 1 byte yer tutarlar.

fn main() {
let x = true;

let y: bool = false;
}

Karakter (char)

char karakterleri temsil eden bir tiptir. Her karakterin bir sayı karşılığı vardır, örneğin büyük harfli "M" nin sayı karşılığı 77, küçük harfli "m" nin ise 109'dur. Bütün bu numara karşılıklarına Unicode denilir. Rust'ta char tipi tek tikli tırnak işaretiyle oluşturulur ve bellekte 4 byte yer kaplarlar.

let my_char = 'm';
let space = ' '; // boşluk da bir char tipidir.
let emoji = '😺' // emojiler de bir char tipidir.

Koleksiyon Tipler

Koleksiyon tiplerde tek bir değer yerine birden fazla değer bir tipe atanır. Bu kısımda iki farklı temel koleksiyon tipini ele alacağız.

Tuple

Tuple, birden fazla değerin bir arada toplanarak tek bir tip oluşturmanın yollarından birisidir. Tuple tipte değerler aynı ya da farklı türde olabilir ve bir kez oluşturulduktan sonra boyutu değiştirilemez, değer ekleme ya da çıkarma yapılamaz. Tuple oluşturmak için değerler iki parantez () arasına yazılır, manuel tip ataması da aynı şekilde yapılır.

fn main() {
let tup: (i32, f64, u8) = (50, 6.8, 1); // tuple elemanları farklı tiplerden oluşabilir
let fifty = tup.0; // tuple elemanlarına index numarası ile erişebiliriz
let (x, y, z) = (12, 56, 3.4); // tuple elemanlarının her birisini bir değişkene atayabiliriz
}

Array (Dizi)

Birden fazla değeri bir tipe atamanın yollarından birisi de dizilerdir. Ancak dizileri oluşturan elemanların türleri aynı olmalıdır. Diziler de tuple gibi sabit boyuttadır, eleman ekleme ve çıkarma yapılamaz. Array (dizi) ataması yapmak için köşeli parantez [] kullanılır. Manuel tip ataması yaparken ilk eleman dizi elemanlarının tipini, ikinci eleman ise dizinin boyutunu (length) belirtir.

fn main() {
let a: [i32; 6] = [1, 2, 3, 4, 5, 6]; // [i32; 6] ilk eleman tip, ikinci eleman boyut (length)
let birinci_eleman = a[0]; // dizinin elamanlarına index numarası ile bu şekilde erişebiliriz
let dorduncu_eleman = a[3]; // index numaraları her zaman 0 dan başlar
}

Bir dizinin elemanları aynı değerden oluşuyorsa o diziyi şu şekilde de başlatabiliriz:

fn main() {
let a = [3; 6]; // dizinin eşdeğeri a = [3, 3, 3, 3, 3, 3]
}

Yukarıdaki kodda dizinin ilk elemanı olan 3 sayısı dizimizin içindeki değeri temsil eder, ikinci elaman olan 6 sayısı ise dizinin boyutunu (length) temsil eder. O halde yukarıdaki dizi altı tane 3 sayısından oluşan bir dizidir.