Kontrol Akışı
Bir koşulun doğru olup olmadığına bağlı olarak bazı kodları çalıştırma veya bir
koşul doğruyken bazı kodları tekrar tekrar çalıştırma yeteneği,
çoğu programlama dili için temel yapı taşıdır. Rust kodunun yürütme akışını kontrol etmenizi
sağlayan en yaygın yapılar if ifadeleri ve döngülerdir.
if İfadeleri
Bir if ifadesi, koşullara bağlı olarak kodunuzu dallandırmanıza olanak tanır.
Bir koşul sağlarsınız ve ardından “Eğer bu koşul karşılanırsa, bu kod bloğunu çalıştırın. Koşul karşılanmazsa, bu kod bloğunu çalıştırmayın” emrini verirsiniz.
if ifadesini keşfetmek için proje dizininizde branches adında yeni bir proje oluşturun.
src/main.rs dosyasına aşağıdakini girin:
Dosya adı: src/main.rs
fn main() { let number = 3; if number < 5 { println!("condition was true"); } else { println!("condition was false"); } }
Tüm if ifadeleri, if anahtar sözcüğüyle başlar ve ardından bir koşul gelir.
Bu durumda koşul, number'ın 5'ten küçük bir değere sahip olup olmadığını kontrol eder.
Koşul doğruysa yürütülecek kod bloğunu, koşulun hemen ardından süslü parantezler içine yerleştiririz.
if ifadelerindeki koşullarla ilişkili kod blokları, tıpkı Bölüm 2'deki “Tahmin ile Gizli Numarayı Karşılaştırma” bölümünde tartıştığımız match ifadelerindeki kollar gibi bazen kol olarak adlandırılır.
İsteğe bağlı olarak, koşulun yanlış olarak değerlendirilmesi durumunda programa yürütülecek alternatif bir kod bloğu
vermek için burada yapmayı seçtiğimiz başka bir ifade de ekleyebiliriz. Başka bir ifade sağlamazsanız ve koşul yanlışsa,
program if bloğunu atlar ve bir sonraki kod parçasına geçer.
Bu kodu çalıştırmayı deneyin, aşağıdaki çıktıyı görmelisiniz:
$ cargo run
Compiling branches v0.1.0 (file:///projects/branches)
Finished dev [unoptimized + debuginfo] target(s) in 0.31s
Running `target/debug/branches`
condition was true
Ne olduğunu görmek için numberın değerini koşulu false yapan bir değerle değiştirmeyi deneyelim:
fn main() {
let number = 7;
if number < 5 {
println!("condition was true");
} else {
println!("condition was false");
}
}
Programı tekrar çalıştırın ve çıktıya bakın:
$ cargo run
Compiling branches v0.1.0 (file:///projects/branches)
Finished dev [unoptimized + debuginfo] target(s) in 0.31s
Running `target/debug/branches`
condition was false
Bu koddaki koşulun bool türünden olması gerektiğini de belirtmekte fayda var.
Koşul bool değilse, bir hata alırız. Örneğin, aşağıdaki kodu çalıştırmayı deneyin:
Dosya adı: src/main.rs
fn main() {
let number = 3;
if number {
println!("number was three");
}
}
if koşulu bu sefer 3 değerini değerlendiriyor ve Rust buna karşılık bir hata veriyor:
$ cargo run
Compiling branches v0.1.0 (file:///projects/branches)
error[E0308]: mismatched types
--> src/main.rs:4:8
|
4 | if number {
| ^^^^^^ expected `bool`, found integer
For more information about this error, try `rustc --explain E0308`.
error: could not compile `branches` due to previous error
Hata, Rust'ın bir bool beklediğini ancak bir tam sayı aldığını gösteriyor.
Ruby ve JavaScript gibi dillerin aksine Rust, Boole olmayan türleri otomatik olarak
Boole'a dönüştürmeye çalışmaz. Açık olmalısınız ve her zaman koşulun Boole olup olmadığını sağlamalısınız.
Örneğin if kod bloğunun yalnızca bir sayı 0'a eşit olmadığında çalışmasını istiyorsak, if ifadesini aşağıdaki gibi değiştirebiliriz:
Dosya adı: src/main.rs
fn main() { let number = 3; if number != 0 { println!("number was something other than zero"); } }
Bu kodu çalıştırmak bize şu çıktıyı verecektir: number was something other than zero.
else if ile Birden Çok Koşulun İşlenmesi
Bir else if ifadesini if ve else ile birleştirerek birden çok koşul durumunda kullanabilirsiniz.
Örneğin:
Dosya adı: src/main.rs
fn main() { let number = 6; if number % 4 == 0 { println!("number is divisible by 4"); } else if number % 3 == 0 { println!("number is divisible by 3"); } else if number % 2 == 0 { println!("number is divisible by 2"); } else { println!("number is not divisible by 4, 3, or 2"); } }
Bu programın alabileceği dört olası durum vardır. Çalıştırdıktan sonra aşağıdaki çıktıyı görmelisiniz
$ cargo run
Compiling branches v0.1.0 (file:///projects/branches)
Finished dev [unoptimized + debuginfo] target(s) in 0.31s
Running `target/debug/branches`
number is divisible by 3
Bu program yürütüldüğünde, sırayla her bir if ifadesini kontrol eder ve koşulun doğru
olduğu ilk gövdeyi yürütür. 6'nın 2'ye bölünebilmesine rağmen, çıktıda number is divisible by 2'yu görmüyor
ve else'e rağmen çıktıda number is not divisible by 4, 3, or 2'yu görmüyoruz.
Bunun nedeni, Rust'ın bloğu yalnızca ilk gerçek koşul için çalıştırmasıdır ve
bir kez doğru koşulu bulduğunda gerisini kontrol bile etmez.
Çok fazla else if ifadesi kullanmak kodunuzu karıştırabilir, bu nedenle birden fazla varsa,
kodunuzu yeniden düzenlemek isteyebilirsiniz. Bölüm 6, bu durumlar için match adı verilen güçlü bir
Rust dallanma yapısını açıklar.
if'i let'te İfade Yapısı Olarak Kullanmak
if bir ifade olduğu için, Liste 3-2'de olduğu gibi sonucu bir değişkene atamak için let ifadesinin sağ tarafında kullanabiliriz.
Dosya adı: src/main.rs
fn main() { let condition = true; let number = if condition { 5 } else { 6 }; println!("The value of number is: {number}"); }
Liste 3-2: Bir if ifadesinin sonucunu bir değişkene atama
number değişkenine, if ifadesinin sonucuna göre bir değer atanacaktır.
Ne olduğunu görmek için bu kodu çalıştırın:
$ cargo run
Compiling branches v0.1.0 (file:///projects/branches)
Finished dev [unoptimized + debuginfo] target(s) in 0.30s
Running `target/debug/branches`
The value of number is: 5
Kod bloklarının içlerindeki son ifadeyi değerlendirdiğini ve sayıların
kendi başlarına da ifadeler olduğunu unutmayın. Bu durumda, if ifadesinin tamamının değeri,
hangi kod bloğunun yürütüldüğüne bağlıdır. Bu, if'in her bir kolundan sonuç alma potansiyeline sahip
değerlerin aynı tür olması gerektiği anlamına gelir; Liste 3-2'de, hem if kolunun hem de else kolunun sonuçları
i32 tam sayı türündendi. Aşağıdaki örnekte olduğu gibi, türler uyumsuzsa bir hata alırız:
Dosya adı: src/main.rs
fn main() {
let condition = true;
let number = if condition { 5 } else { "six" };
println!("The value of number is: {number}");
}
Bu kodu derlemeye çalıştığımızda bir hata alacağız. if ve else kollarının uyumsuz değer türleri vardır
ve Rust, sorunun programda tam olarak nerede bulunacağını bir hata mesajıyla belirtir:
$ cargo run
Compiling branches v0.1.0 (file:///projects/branches)
error[E0308]: `if` and `else` have incompatible types
--> src/main.rs:4:44
|
4 | let number = if condition { 5 } else { "six" };
| - ^^^^^ expected integer, found `&str`
| |
| expected because of this
For more information about this error, try `rustc --explain E0308`.
error: could not compile `branches` due to previous error
if bloğundaki ifade bir tam sayı olarak değerlendirilir ve else bloğundaki ifade bir
dizgi olarak değerlendirilir. Bu işe yaramaz çünkü değişkenlerin tek bir türü olması gerekir ve
Rust'ın derleme zamanında sayı değişkeninin ne tür olduğunu kesin olarak bilmesi gerekir.
Sayının türünü bilmek, derleyicinin sayıyı kullandığımız her yerde türün geçerli olduğunu doğrulamasını sağlar.
Sayının türü yalnızca çalışma zamanında belirlenmiş olsaydı Rust bunu yapamazdı; derleyici daha karmaşık olurdu ve herhangi bir değişken için birden çok varsayımsal türü takip etmesi gerekiyorsa kod hakkında daha az garanti verirdi.
Döngülerle Yinelemek
Bir kod bloğunu bir kereden fazla yürütmek genellikle yararlıdır. Bu görev için Rust, döngü gövdesi içindeki kodu sonuna kadar çalıştıracak ve ardından hemen baştan başlayacak birkaç döngü sağlar. Döngüleri denemek için loops adında yeni bir proje yapalım.
Rust'un üç tür döngüsü vardır: loop, while ve for. Her birini deneyelim.
loop ile Kod Yinelemek
loop anahtar sözcüğü, Rust'a bir kod bloğunu sonsuza kadar veya siz açıkça durmasını söyleyene kadar tekrar tekrar yürütmesini söyler.
Örnek olarak, loops dizininizdeki src/main.rs dosyasını şöyle görünecek şekilde değiştirin:
Dosya adı: src/main.rs
fn main() {
loop {
println!("again!");
}
}
Bu programı çalıştırdığımızda, programı manuel olarak durdurana kadar sürekli olarak again!
yazdırıldığını göreceğiz. Çoğu uçbirim ctrl-c kısayolunu döngüden çıkabilmek
için sunar. Bir şans verin:
$ cargo run
Compiling loops v0.1.0 (file:///projects/loops)
Finished dev [unoptimized + debuginfo] target(s) in 0.29s
Running `target/debug/loops`
again!
again!
again!
again!
^Cagain!
^C sembolü, ctrl-c tuşlarına bastığınız yeri gösterir.
Kesme sinyalini aldığınızda kodun döngüde nerede olduğuna bağlı olarak ^C'den sonra döngü durur.
Neyse ki, Rust ayrıca kod kullanarak bir döngüden çıkmanın bir yolunu da sağlar.
Programa döngüyü yürütmeyi ne zaman durduracağını söylemek için break anahtar sözcüğünü döngünün içine yerleştirebilirsiniz.
Bunu Bölüm 2'deki “Doğru Tahminden Sonra Çıkma” bölümündeki tahmin oyununda, kullanıcı doğru sayıyı tahmin ederek oyunu kazandığında programdan çıkmak için yaptığımızı hatırlayın.
Ayrıca, bir döngüde programa döngünün bu yinelemesinde kalan herhangi bir kodu atlamasını ve bir sonraki yinelemeye geçmesini söyleyen tahmin oyununda continue'ı kullandık.
Döngülerden Değer Döndürmek
loop'un kullanımlarından biri, bir iş parçacığının işini tamamlayıp tamamlamadığını kontrol etmek
gibi başarısız olabileceğini bildiğiniz bir işlemi yeniden denemektir. Ayrıca, bu işlemin sonucunu döngüden kodunuzun geri kalanına aktarmanız gerekebilir. Bunu yapmak için, döngüyü durdurmak için kullandığınız break ifadesinden sonra döndürülmesini istediğiniz değeri ekleyebilirsiniz; bu değer, burada gösterildiği gibi kullanabilmeniz için döngüden döndürülecektir:
fn main() { let mut counter = 0; let result = loop { counter += 1; if counter == 10 { break counter * 2; } }; println!("The result is {result}"); }
Döngüden önce counter adında bir değişken tanımlıyoruz ve onu 0 olarak başlatıyoruz.
Ardından döngüden dönen değeri tutacak result adında bir değişken tanımlıyoruz.
Döngünün her yinelemesinde counter değişkenine 1 ekliyoruz ve ardından counter'ın 10'a eşit olup olmadığını kontrol ediyoruz. Eşitse counter * 2 değerini break anahtar sözcüğüyle kullanıyoruz. Döngüden sonra noktalı virgül kullanıyoruz.
result'a değer atayan ifadeyi bitirmek için; son olarak, 20 olan result değerini yazdırıyoruz.
Birden Çok Döngü Arasındaki Belirsizliği Gidermek için Döngü Etiketleri
Döngüler içinde döngüleriniz varsa, o noktada en içteki döngü için break ve continue ifadeleri uygulanır.
İsteğe bağlı olarak bir döngü üzerinde bir döngü etiketi belirleyebilirsiniz ve daha sonra bu anahtar sözcüklerin en içteki döngü yerine etiketli döngüye uygulanacağını belirtmek için break veya continue ile kullanabiliriz.
İşte iki iç içe döngü içeren bir örnek:
fn main() { let mut count = 0; 'counting_up: loop { println!("count = {count}"); let mut remaining = 10; loop { println!("remaining = {remaining}"); if remaining == 9 { break; } if count == 2 { break 'counting_up; } remaining -= 1; } count += 1; } println!("End count = {count}"); }
Dış döngü 'counting_up etiketine sahiptir ve 0'dan 2'ye kadar sayar.
Etiketsiz iç döngü 10'dan 9'a geri sayım yapar. Bir etiket belirtmeyen ilk break yalnızca iç döngüden çıkar.
break 'counting_up; ifadesi dış döngüden çıkar:
$ cargo run
Compiling loops v0.1.0 (file:///projects/loops)
Finished dev [unoptimized + debuginfo] target(s) in 0.58s
Running `target/debug/loops`
count = 0
remaining = 10
remaining = 9
count = 1
remaining = 10
remaining = 9
count = 2
remaining = 10
End count = 2
while ile Koşullu Döngüler
Bir programın genellikle bir döngü içindeki bir koşulu değerlendirmesi gerekir.
Koşul doğru olduğunda döngü çalışır. Koşul doğru olmadığında, program break'i çağırarak döngüyü durdurur.
Böyle bir davranışı loop, if, else ve break kombinasyonunu kullanarak uygulamak mümkündür;
İsterseniz bunu şimdi bir programda deneyebilirsiniz. Ancak, bu model o kadar yaygındır ki,
Rust'ın bunun için while döngüsü adı verilen yerleşik bir dil yapısı vardır.
Liste 3-3'te, programı üç kez döngüye almak, her seferinde geri saymak ve ardından döngüden sonra bir mesaj yazdırıp çıkmak
için while kullanıyoruz.
Dosya adı: src/main.rs
fn main() { let mut number = 3; while number != 0 { println!("{number}!"); number -= 1; } println!("LIFTOFF!!!"); }
Liste 3-3: Bir koşul doğruyken kodu çalıştırmak için while döngüsü kullanma
Bu yapı, loop, if, else ve break kullandıysanız gerekli olacak birçok iç içe yerleştirmeyi ortadan kaldırır ve daha
nettir. Bir koşul doğru olduğunda kod çalışır; aksi takdirde döngüden çıkar.
for ile Bir Koleksiyonda Yineleme Yapmak
Dizi gibi bir koleksiyonun öğeleri üzerinde döngü oluşturmak için while yapısını kullanmayı seçebilirsiniz.
Örneğin, Liste 3-4'teki döngü a dizisindeki her öğeyi yazdırır.
Dosya adı: src/main.rs
fn main() { let a = [10, 20, 30, 40, 50]; let mut index = 0; while index < 5 { println!("the value is: {}", a[index]); index += 1; } }
Liste 3-4: Bir while döngüsü kullanarak bir koleksiyonun her bir öğesi arasında döngü yapmak
Burada kod, dizideki öğeleri sayar. 0 dizininde başlar ve ardından dizideki son dizine ulaşana kadar döner
(yani index < 5 doğru olmayana kadar). Bu kodu çalıştırmak dizideki her öğeyi yazdıracaktır.
$ cargo run
Compiling loops v0.1.0 (file:///projects/loops)
Finished dev [unoptimized + debuginfo] target(s) in 0.32s
Running `target/debug/loops`
the value is: 10
the value is: 20
the value is: 30
the value is: 40
the value is: 50
Beş dizi değerinin tümü beklendiği gibi terminalde görünür.
Dizin bir noktada 5 değerine ulaşacak olsa da, diziden altıncı bir değer getirmeye çalışmadan önce
döngü kendini yürütmeyi durdurur.
Ancak bu yaklaşım hataya açıktır; index değeri veya test koşulu yanlışsa programın paniğe
kapılmasına neden olabiliriz. Örneğin, a dizisinin tanımını dört öğeye sahip olacak şekilde değiştirdiyseniz
ancak index < 4 iken koşulu güncellemeyi unuttuysanız, kod panikleyecektir.
Ayrıca bu yavaştır, çünkü derleyici döngü boyunca her yinelemede dizinin dizinin sınırları içinde olup
olmadığının koşullu kontrolünü gerçekleştirmek için çalışma zamanı kodu ekler.
Daha özlü bir alternatif olarak, bir for döngüsü kullanabilir ve bir koleksiyondaki her öğe için bir miktar kod çalıştırabilirsiniz. for döngüsü, Liste 3-5'teki koda benzer.
Dosya adı: src/main.rs
fn main() { let a = [10, 20, 30, 40, 50]; for element in a { println!("the value is: {element}"); } }
Liste 3-5: Bir for döngüsü kullanarak bir koleksiyonun her bir öğesi arasında yinelemek
Bu kodu çalıştırdığımızda, Liste 3-4'teki çıktının aynısını göreceğiz. Daha da önemlisi, artık kodun güvenliğini artırdık ve dizinin sonunun ötesine geçmek veya yeterince uzağa gitmemek ve bazı öğeleri kaçırmaktan kaynaklanabilecek hata olasılığını ortadan kaldırdık.
for döngüsünü kullanarak, Liste 3-4'te kullanılan yöntemde olduğu gibi dizideki değerlerin
sayısını değiştirdiyseniz, başka herhangi bir kodu değiştirmeniz gerekmez.
for döngülerinin güvenliği ve kısa olması, onları Rust'ta en yaygın kullanılan
döngü yapısı haline getirir. Liste 3-3'te while döngüsü kullanan geri sayım örneğinde olduğu gibi, bazı kodları belirli sayıda çalıştırmak istediğiniz durumlarda bile, çoğu Rustacean bir for döngüsü kullanır.
Bunu yapmanın yolu, bir sayıdan başlayıp diğer bir sayıdan önce biten tüm sayıları sırayla üreten
standart kütüphane tarafından sağlanan Range tanımını kullanmaktır.
Aralığı tersine çevirmek için bir for döngüsü ve henüz bahsetmediğimiz başka bir yöntem olan rev kullanarak geri
sayım işleminin nasıl görüneceği aşağıda açıklanmıştır:
Dosya adı: src/main.rs
fn main() { for number in (1..4).rev() { println!("{number}!"); } println!("LIFTOFF!!!"); }
Bu kod sizce de daha hoş durmuyor mu?
Özet
Başardın! Bu oldukça büyük bir bölümdü: değişkenler, skaler ve bileşik veri türleri, fonksiyonlar, yorumlar,
if ifadeleri ve döngüler hakkında çokça bilgi edindiniz!
Bu bölümde tartışılan kavramlarla pratik yapmak için aşağıdaki programları oluşturmaya
çalışın:
- Fahrenheit ve Santigrat türleri arasında dönüşüm yapan programı yazın.
- n'inci Fibonaccı sayısını oluşturan programı yazın.
- Şarkıdaki tekrarlardan yararlanarak Noel şarkısı “The Twelve Days of Christmas”'ın sözlerini yazdırın.
Devam etmeye hazır olduğunuzda, Rust'ta diğer programlama dillerinde olmayan bir kavram olan sahiplikten bahsedeceğiz.