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
- 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.
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}");
}
y değişkenini gölgeleyen yeni değişken tanımlayan match koluBurada 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.
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);
}
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.
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);
}
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.
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})");
}
}
}
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.
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");
}
}
}
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");
}
_ => (),
}
}
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.
fn bir_fonksiyon(_: i32, y: i32) {
println!("Bu kod yalnızca y parametresini kullanıyor: {y}");
}
fn main() {
bir_fonksiyon(3, 4);
}
_ kullanmakBu 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:?}");
}
Some içindeki gerçek değeri kullanmadan, yalnızca varyantın kendisiyle eşleşmek için _ kullanmakBurada 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}");
}
}
}
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.
fn main() {
let _x = 5;
let y = 10;
}
_ koymakBurada _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:?}");
}
_ olan kullanılmayan değişken yine de değeri bağlar ve sahipliği alabilirBurada 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:?}");
}
_ 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}"),
}
}
Nokta içindeki yalnızca x alanını kullanıp geri kalanını .. ile yoksaymakBurada x alanı alınır; y ile z ise .. sayesinde tek tek yazılmadan
yoksayılır.
.., demetlerde de çalışır:
fn main() {
let sayilar = (2, 4, 8, 16, 32);
match sayilar {
(birinci, .., son) => {
println!("Bazı sayılar: {birinci}, {son}");
}
}
}
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:
fn main() {
let sayilar = (2, 4, 8, 16, 32);
match sayilar {
(.., ikinci, ..) => {
println!("Bazı sayılar: {ikinci}")
}
}
}
.. sözdizimini belirsiz biçimde kullanma girişimiBu 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 => (),
}
}
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.
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}");
}
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"),
}
}
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}"),
}
}
@ ile değişkene bağlamakBurada 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.