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}"); }
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!!!"); }
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; } }
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}"); } }
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.