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

Desen Sözdizimi

Bu bölümde desenlerde geçerli olan temel sözdizimlerini bir araya toplayacağız ve her birinin ne zaman işe yaradığını göreceğiz.

Sabit Değerlerle Eşleşmek

  1. bölümde gördüğünüz gibi, desenleri sabit değerlerle doğrudan eşleştirebiliriz:
fn main() {
    let x = 1;

    match x {
        1 => println!("bir"),
        2 => println!("iki"),
        3 => println!("üç"),
        _ => println!("herhangi bir şey"),
    }
}

Bu kod bir yazar; çünkü deger içindeki sayı 1’dir. Belirli somut değerlere göre farklı davranmak istediğinizde bu sözdizimi çok kullanışlıdır.

Adlandırılmış Değişkenlerle Eşleşmek

Adlandırılmış değişkenler çürütülemez desenlerdir ve her değerle eşleşir. Kitap boyunca onları çok kullandık. Ama match, if let ve while let içinde kullanırken dikkat edilmesi gereken bir nokta vardır: Bu yapılar yeni bir kapsam başlatır. Dolayısıyla desenin içinde tanımlanan değişkenler, dışarıda aynı ada sahip değişkenleri gölgeler.

19-11 numaralı liste bunun tipik örneğidir.

Filename: src/main.rs
fn main() {
    let x = Some(5);
    let y = 10;

    match x {
        Some(50) => println!("50 alındı"),
        Some(y) => println!("Eşleşti, y = {y}"),
        _ => println!("Varsayılan durum, x = {x:?}"),
    }

    println!("sonda: x = {x:?}, y = {y}");
}
Listing 19-11: Var olan y değişkenini gölgeleyen yeni değişken tanımlayan match kolu

Burada dışarıda y = 10 vardır. Ama match içindeki Some(y) deseni, dışarıdaki yyi kullanmaz; yeni bir y üretir. Bu yeni değişken Some içindeki değere bağlanır. Bu yüzden Some(5) durumunda yazılan çıktı Eşleşti, y = 5 olur; ama match bittikten sonra dışarıdaki y yine 10 olarak kalır.

Eğer dışarıdaki y ile karşılaştırma yapmak istiyorsak, desen içinde aynı adı yeniden tanımlamak yerine match guard kullanmamız gerekir. Bunu birazdan göreceğiz.

Birden Fazla Deseni Eşleştirmek

match içinde | kullanarak birden fazla desen yazabilirsiniz. Bu, desenler arasında veya anlamına gelir:

fn main() {
    let x = 1;

    match x {
        1 | 2 => println!("bir ya da iki"),
        3 => println!("üç"),
        _ => println!("herhangi bir şey"),
    }
}

Bu kod bir ya da iki yazar.

..= ile Değer Aralıklarını Eşleştirmek

..= sözdizimi, kapsayıcı aralıklarla eşleşmemizi sağlar:

fn main() {
    let x = 5;

    match x {
        1..=5 => println!("birden beşe kadar"),
        _ => println!("başka bir şey"),
    }
}

Burada değer 1 ile 5 arasındaysa ilk kol çalışır. Bunu 1 | 2 | 3 | 4 | 5 yerine yazmak çok daha kısadır.

Rust, aralığın boş olup olmadığını yalnızca sayısal türler ve char için derleme zamanında anlayabildiği için, aralık desenleri yalnız bu türlerle kullanılabilir. char örneği şöyle görünür:

fn main() {
    let x = 'c';

    match x {
        'a'..='j' => println!("erken ASCII harfi"),
        'k'..='z' => println!("geç ASCII harfi"),
        _ => println!("başka bir şey"),
    }
}

Burada 'c', ilk aralığa düştüğü için erken ASCII harfi yazılır.

Ayrıştırarak Değeri Parçalarına Bölmek

Desenleri struct, enum ve demetleri parçalara ayırmak için de kullanabiliriz.

Struct’lar

19-12 numaralı listede, Nokta struct’ının alanlarını let ile ayrı değişkenlere ayırıyoruz.

Filename: src/main.rs
struct Nokta {
    x: i32,
    y: i32,
}

fn main() {
    let nokta = Nokta { x: 0, y: 7 };

    let Nokta { x: a, y: b } = nokta;
    assert_eq!(0, a);
    assert_eq!(7, b);
}
Listing 19-12: Bir struct alanlarını ayrı değişkenlere ayırmak

Burada alan adları ile desen içindeki değişken adları aynı olmak zorunda değildir. Ama sık kullanılan kısa biçimde, aynı adı iki kez yazmadan struct alan adı doğrudan kullanılır. 19-13 numaralı listedeki sürüm aynı işi daha kısa yapar.

Filename: src/main.rs
struct Nokta {
    x: i32,
    y: i32,
}

fn main() {
    let nokta = Nokta { x: 0, y: 7 };

    let Nokta { x, y } = nokta;
    assert_eq!(0, x);
    assert_eq!(7, y);
}
Listing 19-13: Struct alanı kısaltması kullanarak ayrıştırmak

Desenin içinde bazı alanları sabit değerle eşleştirip bazılarını değişkene de bağlayabilirsiniz. 19-14 numaralı liste bunu gösteriyor.

Filename: src/main.rs
struct Nokta {
    x: i32,
    y: i32,
}

fn main() {
    let nokta = Nokta { x: 0, y: 7 };

    match nokta {
        Nokta { x, y: 0 } => println!("x ekseni uzerinde: {x}"),
        Nokta { x: 0, y } => println!("y ekseni uzerinde: {y}"),
        Nokta { x, y } => {
            println!("Hicbir eksen uzerinde degil: ({x}, {y})");
        }
    }
}
Listing 19-14: Aynı desende hem ayrıştırmak hem sabit değerlerle eşleştirmek

Burada noktanın x ekseni üzerinde, y ekseni üzerinde ya da hiçbir eksende olmaması farklı kollarda ele alınıyor.

Enum’lar

Kitap boyunca enum ayrıştırması yaptık; ama şimdi bunu açıkça adlandırıyoruz. Bir enum varyantını ayrıştıran desen, o varyantın nasıl tanımlandığına karşılık gelir. 19-15 numaralı listede Mesaj enum’u üzerinde bunu görüyoruz.

Filename: src/main.rs
enum Mesaj {
    Cik,
    Tasi { x: i32, y: i32 },
    Yaz(String),
    RenkDegistir(i32, i32, i32),
}

fn main() {
    let mesaj = Mesaj::RenkDegistir(0, 160, 255);

    match mesaj {
        Mesaj::Cik => {
            println!("Çık varyantında ayrışacak veri yok.");
        }
        Mesaj::Tasi { x, y } => {
            println!("x yönünde {x}, y yönünde {y} taşı");
        }
        Mesaj::Yaz(text) => {
            println!("Metin mesajı: {text}");
        }
        Mesaj::RenkDegistir(r, g, b) => {
            println!("Rengi kırmızı {r}, yeşil {g}, mavi {b} olarak değiştir");
        }
    }
}
Listing 19-15: Farklı türlerde değer taşıyan enum varyantlarını ayrıştırmak

Cik gibi veri taşımayan varyantlarda daha fazla ayrıştırma yapamayız. Struct benzeri varyantlarda süslü parantezli, demet benzeri varyantlarda ise demet desenlerine benzeyen sözdizimi kullanırız.

İç İçe Geçmiş Struct ve Enum’lar

Desenler bir seviyeden daha derine de inebilir. 19-16 numaralı listede Mesaj içindeki Renk enum’unu ayrıştırıyoruz.

enum Renk {
    Rgb(i32, i32, i32),
    Hsv(i32, i32, i32),
}

enum Mesaj {
    Cik,
    Tasi { x: i32, y: i32 },
    Yaz(String),
    RenkDegistir(Renk),
}

fn main() {
    let mesaj = Mesaj::RenkDegistir(Renk::Hsv(0, 160, 255));

    match mesaj {
        Mesaj::RenkDegistir(Renk::Rgb(r, g, b)) => {
            println!("Rengi kırmızı {r}, yeşil {g}, mavi {b} olarak değiştir");
        }
        Mesaj::RenkDegistir(Renk::Hsv(h, s, v)) => {
            println!("Rengi ton {h}, doygunluk {s}, değer {v} olarak değiştir");
        }
        _ => (),
    }
}
Listing 19-16: İç içe enum’lar üzerinde eşleştirme yapmak

Burada tek bir match içinde hem dış enum varyantını hem de onun içindeki enum değerini ayırabiliyoruz.

Struct ve Demetleri Karıştırmak

Ayrıştırma desenleri daha da karmaşık biçimde iç içe kullanılabilir:

fn main() {
    struct Nokta {
        x: i32,
        y: i32,
    }

    let ((ayak, inc), Nokta { x, y }) = ((3, 10), Nokta { x: 3, y: -10 });
}

Bu tür desenler, yalnız ilgilendiğiniz parçaları elde edip geri kalanı bir arada bırakmamak için çok kullanışlıdır.

Desende Değerleri Yoksaymak

Bazen desendeki bazı değerler ilgimizi çekmez. Bunun için birkaç yol vardır: tek başına _, desende iç içe _, başında alt çizgi olan adlar ve ...

Tüm Bir Değeri _ ile Yoksaymak

_, her şeyle eşleşen ama değeri bağlamayan joker desendir. 19-17 numaralı liste fonksiyon parametresinde kullanımını gösteriyor.

Filename: src/main.rs
fn bir_fonksiyon(_: i32, y: i32) {
    println!("Bu kod yalnızca y parametresini kullanıyor: {y}");
}

fn main() {
    bir_fonksiyon(3, 4);
}
Listing 19-17: Fonksiyon imzasında _ kullanmak

Bu kod ilk parametreyi tamamen yok sayar ve yalnızca y değerini kullanır.

İç İçe _ ile Değerin Bir Parçasını Yoksaymak

Bir değerin yalnızca bazı parçalarını önemsiyorsanız, _yi başka desenlerin içinde de kullanabilirsiniz. 19-18 numaralı listedeki örnek bunu gösteriyor.

fn main() {
    let mut ayar_degeri = Some(5);
    let yeni_ayar_degeri = Some(10);

    match (ayar_degeri, yeni_ayar_degeri) {
        (Some(_), Some(_)) => {
            println!("Var olan özelleştirilmiş değerin üzerine yazılamaz");
        }
        _ => {
            ayar_degeri = yeni_ayar_degeri;
        }
    }

    println!("ayar değeri şu: {ayar_degeri:?}");
}
Listing 19-18: Some içindeki gerçek değeri kullanmadan, yalnızca varyantın kendisiyle eşleşmek için _ kullanmak

Burada ayar_degeri ile yeni_ayar_degeri ikisi de Some ise, içlerindeki gerçek sayıları umursamıyoruz; yalnızca ikisinin de Some olmasını önemsiyoruz.

19-19 numaralı liste ise bir demette birden fazla öğeyi yoksaymayı gösteriyor.

fn main() {
    let sayilar = (2, 4, 8, 16, 32);

    match sayilar {
        (birinci, _, ucuncu, _, besinci) => {
            println!("Bazı sayılar: {birinci}, {ucuncu}, {besinci}");
        }
    }
}
Listing 19-19: Bir demetin birden fazla parçasını yoksaymak

Başına _ Koyarak Kullanılmayan Değişkeni Susturmak

Bir değişken oluşturup henüz kullanmıyorsanız Rust normalde uyarı verir. Ama değişken adını _ ile başlatırsanız bu uyarıyı susturabilirsiniz. 19-20 numaralı liste bunu gösteriyor.

Filename: src/main.rs
fn main() {
    let _x = 5;
    let y = 10;
}
Listing 19-20: Kullanılmayan değişken uyarısını önlemek için adın başına _ koymak

Burada _x için uyarı gelmez, ama y için gelir.

Fakat tek başına _ ile _ad arasında önemli fark vardır: _ad yine de değeri bağlar; _ ise bağlamaz. Bu fark sahiplik açısından önemlidir. 19-21 numaralı liste bunu gösterir.

fn main() {
    let dizgi = Some(String::from("Merhaba!"));

    if let Some(_dizgi) = dizgi {
        println!("bir dizgi bulundu");
    }

    println!("{dizgi:?}");
}
Listing 19-21: Başında _ olan kullanılmayan değişken yine de değeri bağlar ve sahipliği alabilir

Burada s değeri _s içine taşındığı için daha sonra yeniden kullanılamaz. Buna karşılık 19-22 numaralı listedeki gibi yalnızca _ kullanırsanız değer bağlanmaz:

fn main() {
    let dizgi = Some(String::from("Merhaba!"));

    if let Some(_) = dizgi {
        println!("bir dizgi bulundu");
    }

    println!("{dizgi:?}");
}
Listing 19-22: Tek başına _ kullanmak değeri bağlamaz

.. ile Kalan Parçaları Yoksaymak

Bir değerin pek çok parçası varsa, .. ile kalan her şeyi topluca yoksayabilirsiniz. 19-23 numaralı liste buna örnektir.

fn main() {
    struct Nokta {
        x: i32,
        y: i32,
        z: i32,
    }

    let koken = Nokta { x: 0, y: 0, z: 0 };

    match koken {
        Nokta { x, .. } => println!("x su: {x}"),
    }
}
Listing 19-23: Nokta içindeki yalnızca x alanını kullanıp geri kalanını .. ile yoksaymak

Burada x alanı alınır; y ile z ise .. sayesinde tek tek yazılmadan yoksayılır.

.., demetlerde de çalışır:

Filename: src/main.rs
fn main() {
    let sayilar = (2, 4, 8, 16, 32);

    match sayilar {
        (birinci, .., son) => {
            println!("Bazı sayılar: {birinci}, {son}");
        }
    }
}
Listing 19-24: Bir demette ilk ve son değeri alıp diğerlerini yoksaymak

Ama .. kullanımı belirsiz olmamalıdır. 19-25 numaralı listedeki gibi iki tarafa birden yaymaya çalışırsanız derleyici hangi öğelerin gerçekten eşleşeceğini anlayamaz:

Filename: src/main.rs
fn main() {
    let sayilar = (2, 4, 8, 16, 32);

    match sayilar {
        (.., ikinci, ..) => {
            println!("Bazı sayılar: {ikinci}")
        }
    }
}
Listing 19-25: .. sözdizimini belirsiz biçimde kullanma girişimi

Bu durumda derleme hatası alırsınız:

    Checking desenler v0.1.0 (/home/hakanbiris/github/kitap/listings/ch19-patterns-and-matching/listing-19-25)
error: `..` can only be used once per tuple pattern
 --> src/main.rs:5:22
  |
5 |         (.., ikinci, ..) => {
  |          --          ^^ can only be used once per tuple pattern
  |          |
  |          previously used here

error: could not compile `desenler` (bin "desenler") due to 1 previous error

Match Guard ile Ek Koşul Eklemek

Match guard, match kolundaki desenden sonra yazılan ek if koşuludur. Desen eşleşse bile, kolun seçilebilmesi için bu koşulun da sağlanması gerekir.

19-26 numaralı listede ilk kolun deseni Some(x), guard’ı ise if x % 2 == 0 koşuludur.

fn main() {
    let sayi = Some(4);

    match sayi {
        Some(x) if x % 2 == 0 => println!("{x} sayısı çifttir"),
        Some(x) => println!("{x} sayısı tektir"),
        None => (),
    }
}
Listing 19-26: Bir desene match guard eklemek

Bu örnek 4 sayısı çifttir yazar. Eğer değer Some(5) olsaydı, ilk desen eşleşse bile guard yanlış olduğu için ikinci kola düşerdi.

Match guard’ın önemli bir kullanımı, gölgelenme sorununu çözmektir. 19-11’de dışarıdaki yyi kullanmak istememize rağmen içteki desen onu gölgeliyordu. 19-27 numaralı liste, bunu guard ile çözüyor.

Filename: src/main.rs
fn main() {
    let x = Some(5);
    let y = 10;

    match x {
        Some(50) => println!("50 alındı"),
        Some(n) if n == y => println!("Eşleşti, n = {n}"),
        _ => println!("Varsayılan durum, x = {x:?}"),
    }

    println!("sonda: x = {x:?}, y = {y}");
}
Listing 19-27: Dışarıdaki değişkenle eşitliği denemek için match guard kullanmak

Burada Some(n) if n == y deseni, yeni bir n tanımlar; ama dışarıdaki y aynı kalır. Böylece gerçekten dış y ile karşılaştırma yapabiliriz.

| ile birden fazla deseni bir guard ile birleştirdiğinizde, guard hepsine bir arada uygulanır. 19-28 numaralı liste bunu gösteriyor.

fn main() {
    let x = 4;
    let y = false;

    match x {
        4 | 5 | 6 if y => println!("evet"),
        _ => println!("hayir"),
    }
}
Listing 19-28: Birden fazla deseni tek guard ile birleştirmek

Buradaki guard, yalnızca 6ya değil, tüm 4 | 5 | 6 grubuna uygulanır.

@ Bağlamaları Kullanmak

@ operatörü, bir değeri hem desene göre sınayıp hem de o anda bir değişkene bağlamamızı sağlar. 19-29 numaralı liste bunun örneğidir.

fn main() {
    enum Mesaj {
        Merhaba { id: i32 },
    }

    let mesaj = Mesaj::Merhaba { id: 5 };

    match mesaj {
        Mesaj::Merhaba { id: id @ 3..=7 } => {
            println!("Aralıkta bir kimlik bulundu: {id}")
        }
        Mesaj::Merhaba { id: 10..=12 } => {
            println!("Başka bir aralıkta kimlik bulundu")
        }
        Mesaj::Merhaba { id } => println!("Başka bir kimlik bulundu: {id}"),
    }
}
Listing 19-29: Bir değeri hem desenle sınayıp hem @ ile değişkene bağlamak

Burada id @ 3..=7, id alanının 3..=7 aralığında olup olmadığını test eder ve eşleşirse aynı değeri id değişkenine bağlar. Böylece hem test hem bağlama tek desende yapılmış olur.

Özet

Rust’taki desenler, farklı veri biçimlerini ayırt etmede son derece güçlüdür. match ile kullanıldıklarında Rust, bütün olası değerleri ele almanızı zorunlu kılar; aksi halde program derlenmez. let ifadeleri ve fonksiyon parametrelerindeki desenler ise değerleri küçük parçalara ayırıp bu parçaları ayrı değişkenlere bağlamanızı sağlar.

İhtiyacınıza göre çok basit ya da oldukça karmaşık desenler kurabilirsiniz.

Sıradaki bölümde, kitabın sondan bir önceki durağında, Rust özelliklerinin çeşitli gelişmiş yönlerine bakacağız.