Rust Programlama Diline Giriş
Rust programlama dili, özellikle güvenilir bir şekilde asenkron işler yapabilmek için tasarlanan, bellek güvenliği öncelikli, çoklu geliştirim yaklaşımlarına sahip bir sistem programlama dilidir. Çoklu geliştirim yaklaşımları olarak: eş zamanlı, fonksiyonel, generic, emirsel ve yapısal programlama dillerinin yeteneklerini bünyesinde barındırır. Söz dizimi olarak C++’a benzemekle birlikte, yüksek bir performans ile güvenli bellek yönetimini gerçekleştirir.
Mozilla Research’te Graydon Hoare tarafından tasarlanan Rust, JavaScript’in geliştiricisi olan Brendan Eich, ve asm.js ile birlikte internet tarayıcısı motoru olan Servo’nun geliştiricisi olan Dave Herman gibi birçok ünlü geliştiricinin katkılarıyla oluşturulmuştur. Rust dilinin tasarımcıları, Servo tarayıcı motorunu yazarken bu dili iyileştirip yeni özellikler eklemişlerdir. Özgür ve açık kaynaklı derleyiciye sahip olan Rust, Stack Overflow geliştirici anketlerinde son 4 yıl üst üste “en çok sevilen yazılım dili” olmayı başarmıştır.
Dilin Tasarımı
Rust dili, asenkron işlemler ağırlıklı, oldukça güvenilir ve büyük ölçekli sistemlerin geliştirimi için tasarlanmıştır. Bu nedenle güvenilirlik, bellek düzeninin kontrolü ve eşzamanlılık gibi birçok özelliği barındıran bir işlem kümesine sahiptir.
Rust dilinin söz dizimi, C ve C++ ile benzerlikler gösterecek şekilde, kod bloklarının birbirinden ayrıldığı süslü parantezleri ve if-else-while-for gibi kontrol akışlarını içerir. C ve C++’a özgü tüm anahtar kelimelerin bulunmasının yanında match
gibi regex kontrolü sağlayan bazı özel kontrol ifadelerini içerir. Söz dizimi C ve C++’a benziyor gibi görünse de, derinlemesine ele alındığında Haskell gibi fonksiyonel dillere daha yakın niteliktedir. Kontrol akışı operatörleri de dahil olmak üzere her bir fonksiyon gövdesi çalıştırılabilir bir kod ifadesidir. Örneğin, Rust’taki sıradan bir if
koşulu, C’deki 3’lü koşul operatörünün (a?x:y) yerini alabilir. Ayrıca fonksiyonların return
ifadesi ile bitmeleri gerekmez, return
ifadesi olmadığı durumda fonksiyon içerisindeki son ifade geri döndürülür.
Bellek Güvenliği
Rust dili bellek güvenli (memory-safe) olarak tasarlandığı için; null işaretçileri, silinen belleği gösteren işaretçiler (dangling pointers), ve birden fazla thread’in veriyi isteme durumu (data races) gibi özellikler içermez. Bu nedenle değişkenler, sadece sabitlenmiş form kümeleri (struct
, enum
, vb.) aracılığıyla atanabilirler. Bu form kümeleri, girdi değerlerinin halihazırda forma verilmiş olmasını şart koşmaktadır. Diğer dillerde bulunan linked list ve binary tree gibi veri yapılarındaki işlevin sağlanması için NULL ifadesi yerine Rust’ta option türü yer almaktadır. Bu tür içerisinde Some
veya None
olan test edilebilir iki adet veriyi barındırmaktadır. Ayrıca Rust’ta nesne yaşam döngüsünü yönetmek için borrow checker adında bir ödünç alma denetimi de mevcuttur.
Bellek Yönetimi
Rust’ın belki de en büyük özelliklerinden biri Java ve .NET’teki gibi ölü nesnelerin işaretçilerinin otomatik olarak kaldırıldığı bir garbage collection sisteminin bulunmamasıdır. Bunun yerine bellek ve diğer kaynaklar, isteğe bağlı atomik referans sayımı ile birlikte, RAII (resource acquisition is initialization) ilkesi ile yönetilirler. Bu sayede Rust, çok küçük bir iş gücü ile kaynakların yönetimini sağlar. Ayrıca Rust, stack üzerinde değişkenlerin tahsis edilmesini kolaylaştırır ve Java gibi diğer dillerde bulunan int’ten Integer’a otomatik dönüştürme (boxing) işlemine de izin vermez.
Ayrıca &
(ampersand) sembolünün kullanıldığı referans kavramı da mevcuttur. Bu referanslar çalışma zamanındaki referans sayımına dahil edilmezler. Bunun yerine derleme zamanında borrow checker tarafından doğrulanırlar. Bu sayede dangling pointer veya diğer beklenmedik davranışlar engellenmiş hale gelmektedir.
Bellek Sahipliği
Rust’ta her verinin benzersiz bir sahibinin olduğu bir sahiplenme (ownership) mekanizması mevcuttur. Bu sistemde verinin scope’u (çalışma alanı), sahibinin scope’u ile aynıdır. Böylece verinin scope’u tamamlandığında, sahibi de boşa çıkmış olur. Değerler, &T
operatörü kullanılarak, immutable (değiştirilmez) referanslar tarafından değişkenlere atanabilirler. Değiştirilebilen (mutable) refarans operatörü olarak, &mutT
veya sadece T
kullanılabilir. Uygulama içerisinde her zaman ya birden çok immutable referans vardır, ya da yalnızca bir adet mutable referans bulunmaktadır. Rust derleyicisi derleme anında referansların doğru olarak atanıp-atanmadığını kontrol ederek bu kuralları uygular.
Veri Tipleri ve Polimorfizim
Rust’taki tip sistemi, Haskell’e benzer şekilde çalışan traits
adı verilen bir mekanizmadan oluşur. Bu mekanizma interface/abstract class gibi çalışır. Değişken tanımlamalarına tip vererek polimorfizmin oluşmasını sağlar. higher kinded polymorphism
gibi Haskell’de bulunan diğer özellikler henüz desteklenmemiştir.
JavaScript ve C#’taki tip belirlemesiz otomatik değişken oluşturma var
ile sağlanırken, Rust’ta bunun için let
anahtar kelimesi kullanılır. Rust’ta let
ile tanımlanan değişkenler, değer atama yapmadan oluşturulabilir ve daha sonra atama yapılabilir. Örn: let a; a = 42;
. Değişkenler varsayılan olarak immutable oldukları için birden fazla atama yapmak istendiğinde let mut a;
şeklinde mut
kelimesi kullanarak yapılmalıdır.
Generic parametre alan fonksiyonlar, genellikle belirli trait
‘leri implement eden generic tiplere ihtiyaç duyar. Bu tür bir fonksiyonda, generic değer yalnızca o trait’ler vasıtasıyla kullanılabilir. Bu bağlamda, generic fonksiyon tanımlandığı anda artık tip kontrolü yapılabilir durumda olur. C++’taki template’lerde ise böyle bir durum yoktur ve somut tipler ile değişken oluşturulana dek duck-typed olarak kalırlar. C++’taki bu sorunun C++20 versiyonu ile birlikte giderileceği öngörülmektedir.
Rust generic’leri C++ template’leri ile benzerlik gösterirler. Her bir değişken oluşturma sonrasında kodun ayrı bir kopyası oluşturulur. Buna monomorphization denir ve C# ve Java’da kullanılan type erasure şemasının tam tersidir. Monomorphization’ın yararı, her bir kullanım durumu için optimize edilmiş bir kodun üretilmesidir. Dezavantajı ise derleme zamanının uzaması ve oluşturulan binary dosyaların boyutunun artmasıdır.
Rust içerisindeki nesne sistemi; implementasyonlar, trait’ler ve yapısal tipler (structured types) üzerine kurulmuştur. Implementasyonlar diğer dillerdeki class yapılarına benzerlik gösterirler ve impl
kelimesi ile oluşturulurlar. Nesne yönelimli diğer dillerde gördüğümüz miras ve çok biçimlilik ise trait’ler tarafından sağlanmıştır. Trait’ler, Java’daki abstract class’lar gibi metodların tanımlanmasını ve implementasyonlar içerisinde kullanılmasını sağlar. Yapısal tipler ise C’deki struct
gibi değişken alanlarının tanımlanması için kullanılırlar. Implementasyonlar ve trait’ler struct gibi değildir ve kendi bünyesinde değişken barındırmazlar. Bunun yanında sadece trait’lerde miras olayı mevcuttur. Bu sayede C++ gibi çoklu miras destekleyen dillerde oluşan diamond problemi engellenmiş olur. Başka bir deyişle Rust, sadece interface üzerinden mirasın yapılmasını sağlar ve bu interface’lerin kompozisyonu sayesinde miras işlevini gerçekleştirir. Burada görülen Composition over inheritance
yani miras yerine kompozisyon yapılması React gibi framework’lerde de oldukça popülerdir.
Kod Örnekleri
Merhaba Dünya
Her programlama dilinin olmazsa olmazı olan “Merhaba Dünya!” örneği, Rust dilinde println!
fonksiyonu kullanılarak yazılır.
fn main() {
println!(“Merhaba Dünya!”);
}
Faktöriyel Alma
Bir sayının, sürekli kendisinin bir eksiği ile çarpılarak oluşan faktör kavramı Rust’ta birçok şekilde yapılabilir.
Öz yinelemeli (recursive) faktöriyel alma
fn faktoriyel(sayi: u64) -> u64 {
match i {
0 => 1,
sayi => sayi * faktoriyel(sayi – 1)
}
}
C++’da ise aynı kodun karşılığı aşağıdaki gibi yer alacaktır:
long faktoriyel(long sayi) {
if (sayi == 0)
return 1;
else
return sayi * faktoriyel(sayi – 1);
}
Rust’ta if
yerine switch
muadili olan match
kullanıldı ve return
ifadeleri olmadan fonksiyondan değer geri dönüşü gerçekleştirildi.
Tekrarlamalı (iterative) Faktöriyel Alma
Rust’ta for
döngüsü kullanılarak iteratif faktöriyel alınabilir:
fn faktoriyel(sayi: u64) -> u64 {
let mut toplam = 1;
for i in 2..=sayi {
toplam *= i;
}
toplam
}
C++’ta ise aşağıdaki gibidir:
long faktoriyel(long n)
{
long toplam = 1;
for (int i = 1; i <= n; i++)
toplam = toplam * i;
return toplam;
}
C++’ın aksine Rust’ta değişkenler immutable oldukları için toplam değişkenini mut
kelimesi kullanarak oluşturmak gerekiyor.
Iterator Kullanarak Faktöriyel Alma
Rust’ta iterator kullanarak faktöriyel işlem yapma bu kadar basittir:
fn faktoriyel(sayi: u64) -> u64 {
(sayi..=i).product()
}
Rust Geliştirim Araçları
Rust ile uygulama geliştirimi sürecini kolaylaştıran birçok araç bulunmaktadır:
- RLS: Rust, VSCode gibi birçok IDE ile entegre çalışmak için RLS’i kullanır. Linting, kod tamamlama, syntax kontrolü ve kod formatlama gibi özellikleri sunar.
- Cargo: Rust için paket yöneticisi ve kod derleme sistemidir. Eğer Rust’ı denediyseniz büyük ihtimalle Cargo’yu da görmüşsünüzdür. Bünyesinde, code coverage gibi birçok ek özellik sunan eklentiler mevcuttur. Ayrıca Cargo, birim ve entegrasyon testleri için kullanılabilir. Derleme ve program bağımlılıkları TOML syntax’ı ile ifade edilir.
- crates.io: Açık kaynaklı Cargo paketlerinin yer aldığı paket kayıt sistemidir. Bütün Rust paketlerine buradan ulaşılabilir.
- rustup Rust yüklemelerinin yönetimi için kullanılır. Kurulum için stabil, beta veya nightly sürümler seçilebilir ve önceki sürümlerden herhangi biri yüklenebilir.
- clippy: Rust için bir linter aracıdır. Yaygın olarak yapılan birçok yazım yanlışını tespit ederek düzgün bir biçimde Rust kodu yazmanızı sağlar.
- rustfmt Rust dili için kod formatlama aracıdır.
- sccache: Derleme sürelerini azaltarak hızlı şekilde aksiyon almayı sağlayan bir compiler cache’idir. RLS ile birlikte çalışmadığı için IDE’nizde kullanamayabilirsiniz.
Rust Kurulumu
Rust kurulumu, rustup aracı ile kolay bir şekilde tamamlanabilmektedir. Eğer macOS, Linux veya Unix temelli bir işletim sisteminiz varsa aşağıdaki komut ile Rustup’ı indirebilir ve Rust’ın kurulumunu gerçekleştirebilirsiniz:
curl https://sh.rustup.rs -sSf | sh
Windows işletim sistemi için chocolatey yardımıyla kurulabilir:
choco install rustup.install
Rust Projesi Oluşturma
cargo new <projeAdi>
komutu kullanılarak yeni bir Rust projesi oluşturulabilir ve run komutu ile çalıştırılabilir:
$ cargo new myRust
Created binary (application) `myRust` package
$ cd myRust/
$ cargo run
Compiling myRust v0.1.0 (/Users/<user>/code/myRust)
Finished dev [unoptimized + debuginfo] target(s) in 0.70s
Running `target/debug/myRust`
Hello, world!
Komut satırı uygulamaları ile ilgili daha fazla bilgi için rust-lang-nursery sitesini ziyaret edebilirsiniz.
Masaüstü uygulama geliştirimi için azul, web servis geliştirimi için ise rocket framework’ünü kullanabilirsiniz. Rust ile ilgili birçok aracın yer aldığı Awesome Rust listesine buradan ulaşabilirsiniz.
Rust ile Geliştirilen Projeler
Servo Engine
Rust ile geliştirilen belki de en önemli proje, Mozilla Vakfı’nın yürütmüş olduğu Servo adındaki internet tarayıcısı motorudur. Halihazırda Firefox Quantum tarayıcısında kullanılan Servo, paralellik ve bellek güvenliği gereksinimleriyle sıfırdan geliştirilmiştir. Servo’nun paralel CSS işleme özelliği Gecko‘ya entegre edilerek neredeyse CSS kodunun tamamının GPU üzerinden işlenmesi sağlanmıştır. Ayrıca Servo’nun WebRender adı verilen render mimarisi de Gecko’ya aktarılarak, en kötü senaryoda dahi 60 FPS’in üzerinde CSS/DOM işleme kapasitesine ulaşılmıştır.
İşletim Sistemleri
Birçok işletim sistemi ve ilişkili bileşenleri Rust ile yazılmıştır:
- Redox
- IntermezzOS
- TockOS
- Tifflin
- RustOS
- QuiltOS
- Rux Siz de üstteki gibi Rust ile işletim sistemi yazmak istiyorsanız muhtemelen başlangıç noktanız Writing an OS in Rust (Second Edition) kitabı olacaktır. İşletim sistemi bileşenleri de aşağıdaki gibidir:
- Magic pocket: Dropbox’ın, Diskotech adı verilen petabayt’lık veri saklama makinelerinde kullanılan bir dosya sistemidir.
- Stratis: Linux için üretilen, kullanımı kolay bir dosya sistemidir.
- Railcar: Oracle tarafından geliştirilen container runtime’ıdır.
- Firecracker: Serverless mimariler için üretilen hızlı ve güvenli microVM’ler sunar.
Muhtelif Araçlar
- exa: Unix ve Linux sistemlerde bulunan
ls
‘nin modern versiyonu olan exa, çeşitli özellikler ve kullanıcı dostu bir arabirim sunuyor. - Microsoft Azure IoT Edge: Azure servislerinin ve yapay zeka işlemlerinin IoT cihazlar üzerinde çalıştırılmasını sağlayan platformdur. Kodun 60 bin satırlık kısmı Rust ile yazılmıştır.
- Tor: Halihazırda Rust ile yazılan kod modüllerini derleyen ve bağlayan bir derleme sistemine sahip olan anonim internet ağı olan tor, C’den güvenlik özellikleri için Rust’a port edilme aşamasındadır.
- tikv: Dağıtık ve transaction’lı bir key-value veritabanı sunan tikv’in neredeyse tamamı Rust ile yazılmıştır.
- Wargroove: Chucklefish firması tarafından geliştirilen bu video oyununun sunucu yazılımı olarak Rust kullanılmıştır.
- xi: Başlangıçta macOS için geliştirilen ve kullanıcı arayüzü olarak Cocoa kullanan xi, neredeyse tamamen Rust ile yazılmıştır.
- deno: JavaScript ve TypeScript için güvenli bir runtime sunan Deno da Rust ile yazılmıştır.
Sonuç olarak
Bu yazımızda sizlere Rust programlama dilini ve getirdiği avantajları anlattık. Son olarak Rust’ın maskotu olan ferris’i aşağıya bırakalım. Eğer soru ve görüşleriniz var ise aşağıdaki yorum bölümünden bizimle paylaşabilirsiniz. Bir sonraki yazımızda görüşmek üzere.