Control Flow

Shartning true yoki yo'qligiga qarab ba'zi kodlarni ishga tushirish va shart true bo'lganda ba'zi kodlarni qayta-qayta ishga tushirish qobiliyati ko'pchilik dasturlash tillarida asosiy building bloklari hisoblanadi. Rust kodining bajarilishini nazorat qilish imkonini beruvchi eng keng tarqalgan konstruksiyalar if expressionlari va looplaridir.

if ifodalari

if ifodasi shartlarga qarab kodingizni branchga ajratish imkonini beradi. Siz shartni taqdim etasiz va keyin shunday deb aytasiz: “Agar bu shart bajarilsa, ushbu kod blokini ishga tushiring. Agar shart bajarilmasa, ushbu kod blokini ishga tushirmang."

If ifodasini oʻrganish uchun loyihalar jildingizda branchlar nomli yangi loyiha yarating. src/main.rs faylida quyidagilarni kiriting:

Fayl nomi: src/main.rs

fn main() {
    let raqam = 3;

    if raqam < 5 {
        println!("shart true edi");
    } else {
        println!("shart true edi");
    }
}

Barcha if expressionlari if kalit so‘zidan boshlanadi, undan keyin shart keladi. Bunday holda, shart raqam o'zgaruvchisi 5 dan kichik qiymatga ega yoki yo'qligini tekshiradi. Agar shart true bo'lsa, biz kod blokini shartdan keyin darhol jingalak qavslar ichiga joylashtiramiz. if expressionlaridagi shartlar bilan bog‘langan kod bloklari ba’zan arms deb ataladi, xuddi biz 2-bobning “Tahminni maxfiy raqam bilan solishtirish” bo‘limida muhokama qilgan match expressionlaridagi qurollar kabi.

Ixtiyoriy ravishda, agar shart false deb baholansa, dasturga bajarilishi uchun muqobil kod blokini berish uchun biz tanlagan else expressionini ham kiritishimiz mumkin. Agar else ifodasini bermasangiz va shart false bo‘lsa, dastur shunchaki if blokini o‘tkazib yuboradi va kodning keyingi bitiga o‘tadi.

Ushbu kodni ishga tushirishga harakat qiling; quyidagi chiqishni ko'rishingiz kerak:

$ cargo run
   Compiling branches v0.1.0 (file:///projects/branchlar)
    Finished dev [unoptimized + debuginfo] target(s) in 0.31s
     Running `target/debug/branchlar`
shart true edi

Keling, nima sodir bo'lishini ko'rish uchun raqam qiymatini shartni false qiladigan qiymatga o'zgartirib ko'raylik:

fn main() {
    let raqam = 7;

    if raqam < 5 {
        println!("shart true edi");
    } else {
        println!("shart false edi");
    }
}

Dasturni qayta ishga tushiring va natijaga qarang:

$ cargo run
   Compiling branches v0.1.0 (file:///projects/branches)
    Finished dev [unoptimized + debuginfo] target(s) in 0.31s
     Running `target/debug/branches`
shart false edi

Shuni ham ta'kidlash kerakki, ushbu koddagi shart bool bo'lishi kerak. Agar shart bool bo'lmasa, biz xatoga yo'l qo'yamiz. Masalan, quyidagi kodni ishga tushirishga harakat qiling:

Fayl nomi: src/main.rs

fn main() {
    let raqam = 3;

    if raqam {
        println!("raqam uchta edi");
    }
}

if sharti bu safar 3 qiymatiga teng bo'ladi va Rust xato qiladi:

$ cargo run
   Compiling branches v0.1.0 (file:///projects/branches)
error[E0308]: mismatched types
 --> src/main.rs:4:8
  |
4 |     if raqam {
  |        ^^^^^^ expected `bool`, found integer

For more information about this error, try `rustc --explain E0308`.
error: could not compile `branches` due to previous error

Xato shuni ko'rsatadiki, Rust bool kutgan, lekin integer(butun) son olgan. Ruby va JavaScript kabi tillardan farqli o'laroq, Rust boolean bo'lmagan turlarni boolean tilga o'zgartirishga avtomatik ravishda urinmaydi. Siz aniq bo'lishingiz va har doim if ni mantiqiy shart sifatida ko'rsatishingiz kerak. Agar biz if kod bloki faqat raqam 0 ga teng bo‘lmaganda ishlashini istasak, masalan, if ifodasini quyidagiga o‘zgartirishimiz mumkin:

Fayl nomi: src/main.rs

fn main() {
    let raqam = 3;

    if raqam != 0 {
        println!("raqam noldan boshqa narsa edi");
    }
}

Ushbu kodni ishga tushirish raqam noldan boshqa narsa edi chop etiladi.

else if bilan bir nechta shartlarni boshqarish

if va else ni else if ifodasida birlashtirib, bir nechta shartlardan foydalanishingiz mumkin.Misol uchun:

Fayl nomi: src/main.rs

fn main() {
    let raqam = 6;

    if raqam % 4 == 0 {
        println!("raqam 4 ga bo'linadi");
    } else if raqam % 3 == 0 {
        println!("raqam 3 ga bo'linadi");
    } else if raqam % 2 == 0 {
        println!("raqam 2 ga bo'linadi");
    } else {
        println!("raqam 4, 3 yoki 2 ga bo'linmaydi");
    }
}

Ushbu dasturda to'rtta yo'l bor. Uni ishga tushirgandan so'ng siz quyidagi chiqishni ko'rishingiz kerak:

$ cargo run
   Compiling branches v0.1.0 (file:///projects/branches)
    Finished dev [unoptimized + debuginfo] target(s) in 0.31s
     Running `target/debug/branches`
raqam 3 ga bo'linadi

Ushbu dastur bajarilganda, u har bir if expressionni navbatma-navbat tekshiradi va shart true deb baholanadigan birinchi tanani bajaradi. E'tibor bering 6, 2 ga bo'linsa ham, biz son 2 ga bo'linmaydi chiqishini ko'rmayapmiz va else blokidagi raqam 4, 3 yoki 2 ga bo'linmaydi matnini ko'rmaymiz.Buning sababi, Rust faqat birinchi true shart uchun blokni bajaradi va bir marta topilsa, qolganlarini ham tekshirmaydi. Juda ko'p else if expressionlaridan foydalanish kodingizni buzishi mumkin, shuning uchun sizda bir nechta bo'lsa, kodingizni qayta tahrirlashni xohlashingiz mumkin. 6-bobda bu holatlar uchun match deb nomlangan kuchli Rust tarmoqli konstruksiyasi tasvirlangan.

let statementida if dan foydalanish

if expression bo‘lganligi sababli, biz 3-2-listdagi kabi natijani o‘zgaruvchiga belgilash uchun let statementining o‘ng tomonida foydalanishimiz mumkin.

Fayl nomi: src/main.rs

fn main() {
    let shart = true;
    let raqam = if shart { 5 } else { 6 };

    println!("Raqamning qiymati: {raqam}");
}

Ro'yxat 3-2: if expressioni natijasini o‘zgaruvchiga tayinlash

raqam o'zgaruvchisi if expressioni natijasiga asoslangan qiymatga bog'lanadi. Nima sodir bo'lishini ko'rish uchun ushbu kodni ishga tushiring:

$ cargo run
   Compiling branches v0.1.0 (file:///projects/branches)
    Finished dev [unoptimized + debuginfo] target(s) in 0.30s
     Running `target/debug/branches`
Raqamning qiymati: 5

Esda tutingki, kod bloklari ulardagi oxirgi expressiongacha evaluate qilianadi va raqamlar o'zlari ham expressionlardir. Bu holda butun if expressionning qiymati qaysi kod bloki bajarilishiga bog'liq. Bu if ning har bir armidan result bo'lish potentsialiga ega bo'lgan qiymatlar bir xil turdagi bo'lishi kerakligini anglatadi; 3-2 ro'yxatda if va else armllarining natijalari i32 butun sonlari edi. Agar turlar mos kelmasa(mismatched), quyidagi misolda bo'lgani kabi, biz xatoga duch kelamiz:

Fayl nomi: src/main.rs

fn main() {
    let shart = true;

    let raqam = if shart { 5 } else { "olti" };

    println!("Raqamning qiymati: {raqam}");
}

Ushbu kodni kompilyatsiya qilmoqchi bo'lganimizda, biz xatoga duch kelamiz. if va else armllari mos kelmaydigan qiymat turlariga ega va Rust muammoni dasturda qayerdan topish mumkinligini aniq ko'rsatadi:

$ 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 raqam = if shart { 5 } else { "olti" };
  |                                 -          ^^^^^ 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 blokidagi expression butun songa, else blokidagi expression esa satrga baholanadi. Bu ishlamaydi, chunki oʻzgaruvchilar bitta turga ega boʻlishi kerak va Rust kompilyatsiya vaqtida raqam oʻzgaruvchisi qaysi turini aniq bilishi kerak. raqam turini bilish kompilyatorga ushbu tur biz raqam ishlatadigan hamma joyda yaroqliligini tekshirish imkonini beradi. Agar raqam turi faqat runtimeda aniqlangan bo'lsa, Rust buni qila olmaydi; kompilyator murakkabroq bo'lar edi va agar u har qanday o'zgaruvchi uchun bir nechta gipotetik turlarni kuzatib borishi kerak bo'lsa, kod haqida kamroq kafolatlar beradi.

Looplar bilan takrorlash

Ko'pincha kod blokini bir necha marta bajarish foydali bo'ladi. Ushbu vazifani bajarish uchun Rust bir nechta looplarni taqdim etadi, ular sikl tanasi ichidagi kod orqali oxirigacha ishlaydi va keyin darhol boshida boshlanadi. Looplar bilan tajriba o'tkazish uchun keling, looplar deb nomlangan yangi loyiha yarataylik.

Rustda uch xil looplar mavjud: loop, while va for. Keling, har birini sinab ko'raylik.

Kodni loop bilan takrorlash

loop kalit so'zi Rustga kod blokini abadiy qayta-qayta bajarishni yoki uni to'xtatishni aniq aytmaguningizcha bajarishni aytadi.

Misol tariqasida, looplar jildingizdagi src/main.rs faylini quyidagicha o'zgartiring:

Fayl nomi: src/main.rs

fn main() {
    loop {
        println!("yana!");
    }
}

Ushbu dasturni ishga tushirganimizda, dasturni qo'lda to'xtatmagunimizcha, yana! so'zi doimiy ravishda chop etilishini ko'ramiz.Aksariyat terminallar uzluksiz siklda ishlab qolgan dasturni to'xtatish uchun ctrl-c klaviatura yorliqlarini qo'llab-quvvatlaydi. Sinab ko'ring:

$ cargo run
   Compiling loops v0.1.0 (file:///projects/looplar)
    Finished dev [unoptimized + debuginfo] target(s) in 0.29s
     Running `target/debug/looplar`
yana!
yana!
yana!
yana!
^Cyana!

^C belgisi ctrl-c tugmalarini bosgan joyni bildiradi. Kod uzilish signalini qabul qilganda siklning qayerda bo'lganiga qarab, ^C dan keyin chop etilgan yana! so'zini ko'rishingiz yoki ko'rmasligingiz mumkin.

Yaxshiyamki, Rust kod yordamida loopdan chiqish yo'lini ham taqdim etadi. Siz dasturga siklni bajarishni qachon to'xtatish kerakligini aytish uchun break kalit so'zini siklga qo'yishingiz mumkin. Eslatib o'tamiz, biz buni 2-bobning ”To'g'ri taxmindan keyin chiqish” bo'limidagi taxminiy o'yinda, foydalanuvchi to'g'ri raqamni taxmin qilish orqali o'yinda g'alaba qozonganida dasturdan chiqish uchun qilganmiz.

Shuningdek, biz taxmin qilish o'yinida continue dan foydalandik, bu siklda dasturga siklning ushbu iteratsiyasida qolgan har qanday kodni o'tkazib yuborish va keyingi iteratsiyaga o'tishni aytadi.

Looplardan qiymatlarni qaytarish(return)

loop dan foydalanishdan biri bu ish bajarilmasligi mumkin bo'lgan operatsiyani qaytadan urinish, masalan, thread o'z ishini tugatganligini tekshirish. Bundan tashqari, ushbu operatsiya natijasini kodingizning qolgan qismiga sikldan o'tkazishingiz kerak bo'lishi mumkin. Buning uchun siklni toʻxtatish uchun foydalanadigan break ifodasidan keyin return qilinishi kerak boʻlgan qiymatni qoʻshishingiz mumkin; bu qiymat loopdan qaytariladi, shuning uchun uni bu yerda ko'rsatilganidek ishlatishingiz mumkin:

fn main() {
    let mut hisoblagich = 0;

    let natija = loop {
        hisoblagich += 1;

        if hisoblagich == 10 {
            break hisoblagich * 2;
        }
    };

    println!("Natija: {natija}");
}

Loopdan oldin biz hisoblagich nomli o‘zgaruvchini e’lon qilamiz va uni 0 ga ishga tushiramiz. Keyin sikldan qaytarilgan qiymatni ushlab turish uchun natija nomli o'zgaruvchini e'lon qilamiz. Loopning har bir iteratsiyasida biz hisoblagich o‘zgaruvchisiga 1 qo‘shamiz va keyin hisoblagich 10 ga teng yoki yo‘qligini tekshiramiz. Bu bo'lganda, biz hisoblagich * 2 qiymati bilan break kalit so'zidan foydalanamiz. Loopdan so'ng biz natija qiymatini belgilaydigan statementni tugatish uchun nuqta-verguldan foydalanamiz. Nihoyat, biz qiymatni natijada chop qilamiz, bu holda 20.

Bir nechta looplar orasidagi farqni ajratish uchun loop labellari

Agar sizda looplar ichida looplaringiz bo'lsa, o'sha nuqtada eng ichki loopga break va continue amallari qo'llaniladi. Siz ixtiyoriy ravishda siklda loop label belgilashingiz mumkin, undan so‘ng break yoki continue bilan o‘sha kalit so‘zlar eng ichki loop o‘rniga belgilangan loopga qo‘llanilishini belgilashingiz mumkin. Loop labellari bitta tirnoqcha bilan boshlanishi kerak. Mana ikkita ichki loop bilan bir misol:

fn main() {
    let mut hisob = 0;
    'hisoblash: loop {
        println!("hisob = {hisob}");
        let mut qolgan = 10;

        loop {
            println!("qolgan = {qolgan}");
            if qolgan == 9 {
                break;
            }
            if hisob == 2 {
                break 'hisoblash;
            }
            qolgan -= 1;
        }

        hisob += 1;
    }
    println!("Yakuniy hisob = {hisob}");
}

Tashqi loopda 'hisoblash labeli bor va u 0 dan 2 gacha hisoblanadi. Labelsiz ichki loop 10 dan 9 gacha hisoblanadi. Label ko'rsatilmagan birinchi break faqat ichki sikldan chiqadi. break 'hisoblash; statementi tashqi sikldan chiqadi. Keling kodni run qilib ko'ramiz:

$ cargo run
   Compiling loops v0.1.0 (file:///projects/loops)
    Finished dev [unoptimized + debuginfo] target(s) in 0.58s
     Running `target/debug/loops`
hisob = 0
qolgan = 10
qolgan = 9
hisob = 1
qolgan = 10
qolgan = 9
hisob = 2
qolgan = 10
Yakuniy hisob = 2

while bilan shartli looplar

Dastur ko'pincha loop ichidagi shartni evaluate qilishi kerak bo'ladi. Shart true bo'lsa-da, loop ishlaydi. Shart true bo'lishni to'xtatganda, dastur loopni to'xtatib, break ni chaqiradi. Bu kabi xatti-harakatlarni loop, if, else va break kombinatsiyasidan foydalanib amalga oshirish mumkin; Agar xohlasangiz, buni hozir dasturda sinab ko'rishingiz mumkin. Biroq, bu pattern shunchalik keng tarqalganki, Rustda buning uchun while sikli deb ataladigan o'rnatilgan til konstruktsiyasi mavjud. 3-3 ro'yxatda biz dasturni uch marta aylanish uchun while dan foydalanamiz, har safar sanab chiqamiz, so'ngra sikldan so'ng xabarni chop etamiz va chiqamiz.

Fayl nomi: src/main.rs

fn main() {
    let mut raqam = 3;

    while raqam != 0 {
        println!("{raqam}!");

        raqam -= 1;
    }

    println!("LIFTOFF!!!");
}

Ro'yxat 3-3: Shart to'g'ri bo'lganda kodni ishga tushirish uchun while siklidan foydalanish

Bu konstruksiya loop, if, else va break dan foydalansangiz, zarur bo'ladigan ko'plab joylashtirishlarni yo'q qiladi va bu aniqroq bo'ladi. Shart true deb baholansa, kod ishlaydi; aks holda, u loopdan chiqadi.

for bilan to'plam bo'ylab aylanish

Siz while konstruksiyasidan array kabi to‘plam elementlari ustidan aylanishni tanlashingiz mumkin. Masalan, 3-4 ro'yxatdagi sikl a arrayidagi har bir elementni chop etadi.

Fayl nomi: src/main.rs

fn main() {
    let a = [10, 20, 30, 40, 50];
    let mut index = 0;

    while index < 5 {
        println!("qiymati: {}", a[index]);

        index += 1;
    }
}

Ro'yxat 3-4: while sikli yordamida to‘plamning har bir elementi bo‘ylab aylanish

Bu erda kod arraydagi elementlar orqali hisoblanadi. U 0 indeksidan boshlanadi va keyin arraydagi yakuniy indeksga yetguncha (ya'ni, index < 5 endi true bo`lmaganda) sikl davom etadi. Ushbu kodni ishga tushirish arraydagi har bir elementni chop etadi:

$ cargo run
   Compiling looplar v0.1.0 (file:///projects/looplar)
    Finished dev [unoptimized + debuginfo] target(s) in 0.32s
     Running `target/debug/looplar`
qiymati: 10
qiymati: 20
qiymati: 30
qiymati: 40
qiymati: 50

Barcha besh array qiymatlari kutilganidek terminalda paydo bo'ladi. Garchi index bir nuqtada 5 qiymatiga yetsa ham, arraydan oltinchi qiymatni olishga urinishdan oldin sikl ishlashni to‘xtatadi.

Biroq, bu yondashuv xatoga moyil; Agar indeks qiymati yoki test holati noto'g'ri bo'lsa, biz dasturni panic qo'yishimiz mumkin. Misol uchun, agar siz a arrayining ta'rifini to'rtta elementga o'zgartirsangiz, lekin shartni while index < 4 bo'lganda yangilashni unutgan bo'lsangiz, kod panic qo'zg'atadi. Bu ham sekin, chunki kompilyator sikl orqali har bir iteratsiyada indeks array chegaralarida ekanligini shartli tekshirish uchun runtime kodini qo‘shadi.

Aniqroq variant sifatida, siz for siklidan foydalanishingiz va to'plamdagi har bir element uchun ba'zi kodlarni bajarishingiz mumkin. for sikli 3-5-ro'yxatdagi kodga o'xshaydi.

Fayl nomi: src/main.rs

fn main() {
    let a = [10, 20, 30, 40, 50];

    for element in a {
        println!("qiymati: {element}");
    }
}

Ro'yxat 3-5: for `sikli yordamida to'plamning har bir elementi bo'ylab aylanish

Ushbu kodni ishga tushirganimizda, biz 3-4 ro'yxatdagi kabi natijani ko'ramiz. Eng muhimi, biz kodning xavfsizligini oshirdik va arrayning oxiridan tashqariga chiqish yoki yetarlicha uzoqqa bormaslik va ba'zi elementlarni yetishmayotganligi sababli paydo bo'lishi mumkin bo'lgan xatolar ehtimolini yo'q qildik.

for siklidan foydalanib, agar siz 3-4 roʻyxatda qoʻllanilgan metodda boʻlgani kabi arraydagi qiymatlar sonini oʻzgartirsangiz, boshqa kodni oʻzgartirishni eslab qolishingiz shart emas.

for looplarining xavfsizligi va ixchamligi ularni Rustda eng ko‘p ishlatiladigan loop konstruksiyasiga aylantiradi. 3-3 ro'yxatdagi while siklidan foydalanilgan ortga hisoblash misolida bo'lgani kabi, ma'lum bir necha marta kodni ishlatmoqchi bo'lgan vaziyatlarda ham ko'pchilik Rustaceanlar for siklidan foydalanadilar. Buning yo'li standart kutubxona tomonidan taqdim etilgan Range dan foydalanish bo'lib, bir raqamdan boshlanib, boshqa raqamdan oldin tugaydigan barcha raqamlarni ketma-ketlikda hosil qiladi.

Ortga hisoblash for sikli va biz hali u to‘g‘risida gapirmagan boshqa metod – rev yordamida diapazonni teskari tomonga o‘zgartirishga o‘xshaydi:

Fayl nomi: src/main.rs

fn main() {
    for raqam in (1..4).rev() {
        println!("{raqam}!");
    }
    println!("LIFTOFF!!!");
}

Bu kod biroz chiroyliroq, shunday emasmi?

Xulosa

Siz erishdingiz! Bu juda katta bob bo'ldi: siz o'zgaruvchilar, skalyar va compound ma'lumotlar turlari, funksiyalar, izohlar, if expressionlari va sikllar haqida bilib oldingiz! Ushbu bobda muhokama qilingan tushunchalar bilan mashq qilish uchun quyidagilarni amalga oshirish uchun dasturlar yaratishga harakat qiling:

  • Haroratni Farengeyt va Selsiy o'rtasida o'zgartiring.
  • nta Fibonachchi raqamini yarating.
  • Qo'shiqning takrorlanishidan foydalanib, “Rojdestvoning o'n ikki kuni“ Rojdestvo qo'shig'ining so'zlarini chop eting.

Davom etishga tayyor bo'lganingizda, Rustda boshqa dasturlash tillarida odatda mavjud bo'lmagan ownership(egalik) tushunchasi haqida gaplashamiz.