Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Fonksiyonlar

Fonksiyonlar Rust kodunda çok yaygındır. Dildeki en önemli fonksiyonlardan birini zaten gördünüz: birçok programın giriş noktası olan main fonksiyonu. Ayrıca, yeni fonksiyonlar bildirmenizi sağlayan fn anahtar kelimesini de gördünüz.

Rust kodu, fonksiyon ve değişken isimleri için geleneksel stil olarak snake_case (yılan stili) kullanır; bu stilde tüm harfler küçüktür ve kelimeler alt çizgiyle ayrılır. İşte örnek bir fonksiyon tanımı içeren bir program:

Dosya adı: src/main.rs

fn main() {
    println!("Merhaba, dünya!");

    baska_bir_fonksiyon();
}

fn baska_bir_fonksiyon() {
    println!("Baska bir fonksiyon.");
}

Rust’ta bir fonksiyonu, fn ifadesini, ardından fonksiyon adını ve bir çift parantezi yazarak tanımlarız. Süslü parantezler, derleyiciye fonksiyon gövdesinin nerede başlayıp nerede bittiğini söyler.

Tanımladığımız herhangi bir fonksiyonu, adını ve ardından bir çift parantez yazarak çağırabiliriz. baska_bir_fonksiyon programda tanımlandığı için main fonksiyonunun içinden çağrılabilir. baska_bir_fonksiyon’u kaynak kodunda main fonksiyonundan sonra tanımladığımıza dikkat edin; ondan önce de tanımlayabilirdik. Rust, fonksiyonlarınızı nerede tanımladığınızla ilgilenmez, yalnızca çağırıcı tarafından görülebilen bir kapsamda bir yerlerde tanımlanmış olmalarına bakar.

Fonksiyonları daha fazla keşfetmek için fonksiyonlar adında yeni bir ikili proje başlatalım. baska_bir_fonksiyon örneğini src/main.rs dosyasına yerleştirin ve çalıştırın. Aşağıdaki çıktıyı görmelisiniz:

$ cargo run
   Compiling functions v0.1.0 ($PROJE/listings/ch03-common-programming-concepts/no-listing-16-functions)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.09s
     Running `target/debug/functions`
Merhaba, dünya!
Baska bir fonksiyon.

Satırlar, main fonksiyonunda göründükleri sıraya göre çalıştırılır. Önce “Merhaba, dünya!” mesajı yazdırılır ve ardından baska_bir_fonksiyon çağrılır ve onun mesajı yazdırılır.

Parametreler

Fonksiyonları, bir fonksiyonun imzasının bir parçası olan özel değişkenler olan parametreleri olacak şekilde tanımlayabiliriz. Bir fonksiyonun parametreleri olduğunda, o parametreler için fonksiyona somut (concrete) değerler sağlayabilirsiniz. Teknik olarak, somut değerlere argümanlar denir, ancak günlük konuşmada insanlar parametre ve argüman kelimelerini bir fonksiyonun tanımındaki değişkenler veya bir fonksiyonu çağırdığınızda iletilen somut değerler için birbirinin yerine kullanma eğilimindedir.

baska_bir_fonksiyon’un bu versiyonunda bir parametre ekliyoruz:

Dosya adı: src/main.rs

fn main() {
    baska_bir_fonksiyon(5);
}

fn baska_bir_fonksiyon(x: i32) {
    println!("x'in değeri: {x}");
}

Bu programı çalıştırmayı deneyin; aşağıdaki çıktıyı almalısınız:

$ cargo run
   Compiling functions v0.1.0 ($PROJE/listings/ch03-common-programming-concepts/no-listing-17-functions-with-parameters)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.09s
     Running `target/debug/functions`
x'in değeri: 5

baska_bir_fonksiyon bildiriminin x adında bir parametresi vardır. x’in türü i32 olarak belirtilmiştir. baska_bir_fonksiyon’a 5 değerini verdiğimizde, println! makrosu 5’i, biçim dizisinde (format string) x içeren süslü parantez çiftinin bulunduğu yere koyar.

Fonksiyon imzalarında, her parametrenin türünü mutlaka bildirmelisiniz. Bu, Rust’ın tasarımında alınmış bilinçli bir karardır: Fonksiyon tanımlarında tür bildirimi gerektirmek, derleyicinin kodun başka bir yerinde ne tür bir veri demek istediğinizi anlamak için bu bildirimleri kullanmanıza neredeyse hiç ihtiyaç duymayacağı anlamına gelir. Derleyici ayrıca, fonksiyonun hangi türleri beklediğini bilirse daha yararlı hata mesajları da verebilir.

Birden fazla parametre tanımlarken, parametre bildirimlerini şu şekilde virgüllerle ayırın:

Dosya adı: src/main.rs

fn main() {
    etiketli_olcum_yazdir(5, 'h');
}

fn etiketli_olcum_yazdir(deger: i32, birim_etiketi: char) {
    println!("Ölçüm değeri: {deger}{birim_etiketi}");
}

Bu örnek, iki parametreye sahip etiketli_olcum_yazdir adında bir fonksiyon oluşturur. İlk parametre deger olarak adlandırılır ve bir i32’dir. İkincisi birim_etiketi (unit_label) olarak adlandırılır ve char türündedir. Fonksiyon daha sonra hem deger hem de birim_etiketi’ni içeren bir metin yazdırır.

Hadi bu kodu çalıştırmayı deneyelim. fonksiyonlar projenizin src/main.rs dosyasında bulunan mevcut programı yukarıdaki örnekle değiştirin ve cargo run komutunu kullanarak çalıştırın:

$ cargo run
   Compiling functions v0.1.0 ($PROJE/listings/ch03-common-programming-concepts/no-listing-18-functions-with-multiple-parameters)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.09s
     Running `target/debug/functions`
Ölçüm değeri: 5h

Fonksiyonu deger için 5 ve birim_etiketi için 'h' ile çağırdığımız için, program çıktısı bu değerleri içerir.

İfadeler (Statements) ve İbareler (Expressions)

Fonksiyon gövdeleri, isteğe bağlı olarak bir ibare ile biten bir dizi ifadeden (statements) oluşur. Şimdiye kadar ele aldığımız fonksiyonlar bitirici bir ibare (ending expression) içermiyordu, ancak bir ifadenin bir parçası olarak bir ibare gördünüz. Rust ibare tabanlı bir dil olduğu için, bu anlaşılması gereken önemli bir ayrımdır. Diğer dillerin aynı ayrımları yoktur, bu yüzden ifadelerin ve ibarelerin ne olduğuna ve farklılıklarının fonksiyonların gövdelerini nasıl etkilediğine bir bakalım.

  • İfadeler (Statements), bir eylem gerçekleştiren ancak bir değer döndürmeyen talimatlardır.
  • İbareler (Expressions) ise bir değer üretecek şekilde hesaplanan kod parçalarıdır.

Bazı örneklere bakalım.

Aslında daha önce de ifadeler (statements) ve ibareler (expressions) kullandık. Bir değişken oluşturmak ve let anahtar kelimesiyle ona bir değer atamak bir ifadedir. Liste 3-1’de, let y = 6; bir ifadedir.

Filename: src/main.rs
fn main() {
    let y = 6;
}
Listing 3-1: Tek bir ifade (statement) içeren bir main fonksiyonu bildirimi

Fonksiyon tanımları da ifadelerdir; önceki örneğin tamamı başlı başına bir ifadedir. (Kısaca göreceğimiz gibi, bir fonksiyon çağırmak bir ifade (statement) değildir.)

İfadeler (statements) değer döndürmezler. Bu nedenle, aşağıdaki kodun yapmaya çalıştığı gibi bir let ifadesini başka bir değişkene atayamazsınız; bir hata alırsınız:

Dosya adı: src/main.rs

fn main() {
    let x = (let y = 6);
}

Bu programı çalıştırdığınızda alacağınız hata şu şekilde görünecektir:

$ cargo run
   Compiling functions v0.1.0 ($PROJE/listings/ch03-common-programming-concepts/no-listing-19-statements-vs-expressions)
error: expected expression, found `let` statement
 --> src/main.rs:2:14
  |
2 |     let x = (let y = 6);
  |              ^^^
  |
  = note: only supported directly in conditions of `if` and `while` expressions

warning: unnecessary parentheses around assigned value
 --> src/main.rs:2:13
  |
2 |     let x = (let y = 6);
  |             ^         ^
  |
  = note: `#[warn(unused_parens)]` (part of `#[warn(unused)]`) on by default
help: remove these parentheses
  |
2 -     let x = (let y = 6);
2 +     let x = let y = 6 ;
  |

warning: `functions` (bin "functions") generated 1 warning
error: could not compile `functions` (bin "functions") due to 1 previous error; 1 warning emitted

let y = 6 ifadesi bir değer döndürmez, bu yüzden x’in bağlanacağı bir şey yoktur. Bu durum, atamanın (assignment) atamanın kendi değerini döndürdüğü C ve Ruby gibi diğer dillerde olanlardan farklıdır. Bu dillerde, x = y = 6 yazarak hem x hem de y’nin 6 değerine sahip olmasını sağlayabilirsiniz; Rust’ta ise durum böyle değildir.

İbareler (Expressions) ise bir değere dönüşür ve Rust’ta yazacağınız kodun büyük bir bölümünü oluştururlar. Örneğin 5 + 6 gibi, 11 değerini üreten matematiksel bir işlemi (ibareyi) ele alalım. İbareler, ifadelerin parçası olabilir: Liste 3-1’de, let y = 6; ifadesindeki 6, 6 değerini üreten bir ibaredir. Bir fonksiyonu çağırmak bir ibaredir. Bir makroyu çağırmak bir ibaredir. Süslü parantezlerle oluşturulan yeni bir kapsam bloğu da bir ibaredir, örneğin:

Dosya adı: src/main.rs

fn main() {
    let y = {
        let x = 3;
        x + 1
    };

    println!("y'nin değeri: {y}");
}

Şu ibare:

{
    let x = 3;
    x + 1
}

Bu örnekte 4 değerini üreten (evaluates) bir bloktur. Bu değer, let ifadesinin bir parçası olarak y’ye bağlanır. x + 1 satırının sonunda, şimdiye kadar gördüğünüz çoğu satırın aksine noktalı virgül (;) olmadığına dikkat edin. İbarelerin (expressions) sonunda noktalı virgül bulunmaz. Bir ibarenin sonuna noktalı virgül eklerseniz, onu bir ifadeye (statement) dönüştürürsünüz ve bu durumda bir değer döndürmez. Sırada, fonksiyonların dönüş değerlerini (return values) ve ibareleri incelerken bunu aklınızda bulundurun.

Dönüş Değerleri Olan Fonksiyonlar (Functions with Return Values)

Fonksiyonlar, kendilerini çağıran koda değerler döndürebilirler. Dönüş değerlerini adlandırmayız, ancak türlerini bir ok işaretinden (->) sonra bildirmemiz (declare) gerekir. Rust’ta, bir fonksiyonun dönüş değeri, fonksiyonun gövdesinin bloğundaki son ibarenin değeriyle eş anlamlıdır. return anahtar kelimesini kullanarak ve bir değer belirterek bir fonksiyondan erkenden dönebilirsiniz (return early), ancak çoğu fonksiyon son ibareyi örtük olarak (implicitly) döndürür. İşte bir değer döndüren bir fonksiyon örneği:

Dosya adı: src/main.rs

fn bes() -> i32 {
    5
}

fn main() {
    let x = bes();

    println!("x'in değeri: {x}");
}

bes (five) fonksiyonunun içinde hiçbir fonksiyon çağrısı, makro ve hatta let ifadesi (statement) yoktur—sadece 5 sayısı vardır. Bu, Rust’ta tamamen geçerli bir fonksiyondur. Fonksiyonun dönüş türünün de -> i32 olarak belirtildiğine dikkat edin. Bu kodu çalıştırmayı deneyin; çıktı şu şekilde görünmelidir:

$ cargo run
   Compiling functions v0.1.0 ($PROJE/listings/ch03-common-programming-concepts/no-listing-21-function-return-values)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.09s
     Running `target/debug/functions`
x'in değeri: 5

bes fonksiyonundaki 5, fonksiyonun dönüş değeridir ve bu yüzden dönüş türü i32’dir. Bunu biraz daha detaylı inceleyelim. İki önemli kısım vardır: İlk olarak, let x = bes(); satırı bir değişkeni başlatmak (initialize) için bir fonksiyonun dönüş değerini kullandığımızı gösterir. bes fonksiyonu 5 döndürdüğü için, bu satır şununla aynıdır:

#![allow(unused)]
fn main() {
let x = 5;
}

İkincisi, bes fonksiyonunun hiçbir parametresi yoktur ve dönüş değerinin türünü tanımlar, ancak fonksiyonun gövdesi, döndürmek istediğimiz değere sahip bir ibare olduğu için, noktalı virgülü olmayan yalnız bir 5’tir.

Başka bir örneğe bakalım:

Dosya adı: src/main.rs

fn main() {
    let x = arti_bir(5);

    println!("x'in değeri: {x}");
}

fn arti_bir(x: i32) -> i32 {
    x + 1
}

Bu kod çalıştırıldığında x'in değeri: 6 (The value of x is: 6) yazdıracaktır. Peki x + 1 içeren satırın sonuna bir noktalı virgül koyarsak ve onu bir ibareden bir ifadeye (statement) çevirirsek ne olur?

Dosya adı: src/main.rs

fn main() {
    let x = arti_bir(5);

    println!("x'in değeri: {x}");
}

fn arti_bir(x: i32) -> i32 {
    x + 1;
}

Bu kodu derlemek aşağıdaki gibi bir hata üretecektir:

$ cargo run
   Compiling functions v0.1.0 ($PROJE/listings/ch03-common-programming-concepts/no-listing-23-statements-dont-return-values)
error[E0308]: mismatched types
 --> src/main.rs:7:24
  |
7 | fn arti_bir(x: i32) -> i32 {
  |    --------            ^^^ expected `i32`, found `()`
  |    |
  |    implicitly returns `()` as its body has no tail or `return` expression
8 |     x + 1;
  |          - help: remove this semicolon to return this value

For more information about this error, try `rustc --explain E0308`.
error: could not compile `functions` (bin "functions") due to 1 previous error

Ana hata mesajı olan mismatched types (uyumsuz türler), bu kodun temel sorununu ortaya koyar. arti_bir (plus_one) fonksiyonunun tanımı bir i32 döndüreceğini söyler, ancak ifadeler (statements) bir değer üretmez; bu durum birim türü olan () ile ifade edilir. Bu nedenle, hiçbir şey döndürülmez, bu da fonksiyon tanımıyla çelişir ve bir hataya neden olur. Bu çıktıda Rust, muhtemelen bu sorunu düzeltmeye yardımcı olacak bir mesaj sağlar: Noktalı virgülün kaldırılmasını önerir, ki bu da hatayı düzeltecektir.