Structlar yordamida namunaviy dastur
Structlarni qachon ishlatishimiz mumkinligini tushunish uchun to'rtburchakning maydonini hisoblaydigan dastur yozaylik. Biz bitta o'zgaruvchilardan foydalanishni boshlaymiz, so'ngra uning o'rniga structlardan foydalanmagunimizcha dasturni qayta yaxshilab boramiz.
Keling, cargo bilan kvadratlar deb nomlangan yangi binary loyihani yarataylik, u piksellarda ko'rsatilgan to'rtburchakning kengligi va balandligini oladi va to'rtburchakning maydonini hisoblaydi. 5-8 ro'yxatda loyihaning src/main.rs faylida nima qilishimiz kerakligini aniq bajarishga imkon beradigan bitta qisqa kod ko'rsatilgan.
Fayl nomi: src/main.rs
fn main() { let kenglik1 = 30; let balandlik1 = 50; println!( "To'rtburchakning maydoni {} kvadrat piksel.", area(kenglik1, balandlik1) ); } fn area(kenglik: u32, balandlik: u32) -> u32 { kenglik * balandlik }
Endi ushbu dasturni cargo run
yordamida ishga tushiring:
$ cargo run
Compiling rectangles v0.1.0 (file:///projects/rectangles)
Finished dev [unoptimized + debuginfo] target(s) in 0.42s
Running `target/debug/rectangles`
To'rtburchakning maydoni 1500 kvadrat piksel.
Ushbu kod har bir o'lcham bilan area
funksiyasini chaqirish orqali to'rtburchakning maydonini aniqlashga muvaffaq bo'ladi, ammo biz ushbu kodni aniq va o'qilishi uchun ko'proq narsani qilishimiz mumkin.
Ushbu kod bilan bog'liq muammo area
signaturesida aniq ko'rinadi:
fn main() {
let kenglik1 = 30;
let balandlik1 = 50;
println!(
"To'rtburchakning maydoni {} kvadrat piksel.",
area(kenglik1, balandlik1)
);
}
fn area(kenglik: u32, balandlik: u32) -> u32 {
kenglik * balandlik
}
area
funksiyasi bitta to'rtburchakning maydonini hisoblashi kerak, lekin biz yozgan funksiya ikkita parametrga ega va bizning dasturimizning hech bir joyida parametrlar o'zaro bog'liqligi aniq emas. Kenglik va balandlikni birgalikda guruhlash yanada o'qilishi va boshqarilishi oson bo'lishi mumkin edi.3-bobning ”Tuple Turi” bo'limida biz buni amalga oshirishning bir usulini, ya'ni tuplelardan foydalanishni muhokama qildik.
Tuplelar yordamida Refaktoring
5-9 ro'yxatda tuplelardan foydalanadigan dasturimizning boshqa versiyasi ko'rsatilgan.
Fayl nomi: src/main.rs
fn main() { let kvadrat1 = (30, 50); println!( "To'rtburchakning maydoni {} kvadrat pikselga teng.", area(kvadrat1) ); } fn area(olchamlari: (u32, u32)) -> u32 { olchamlari.0 * olchamlari.1 }
Bir tomondan, bu dastur yaxshiroq. Tuplar bizga biroz struct qo'shishga imkon beradi va biz hozir faqat bitta argumentni keltiramiz. Ammo boshqa yo'l bilan, bu versiya unchalik aniq emas: tuplelar o'z elementlarini nomlamaydi, shuning uchun biz hisob-kitobimizni kamroq aniq qilib, tuple qismlariga indeks qilishimiz kerak.
Kenglik va balandlikni aralashtirish maydonni hisoblash uchun muhim emas, lekin agar biz ekranda to'rtburchak chizmoqchi bo'lsak, bu muhim bo'ladi! Shuni yodda tutishimiz kerakki, kenglik
indeks 0
da, balandlik
esa 1
indeksda. Agar kimdir bizning kodimizdan foydalansa, buni tushunish va yodda tutish qiyinroq bo'ladi. Kodimizda ma'lumotlarimizning ma'nosini etkazmaganimiz sababli, endi xatolarni kiritish osonroq.
Struktuctlar bilan Refaktoring: ko'proq ma'no qo'shish
Biz ma'lumotlarni etiketlash orqali ma'no qo'shish uchun structlardan foydalanamiz. Biz foydalanayotgan tupleni 5-10 ro'yxatda ko'rsatilganidek, butun nomi bilan bir qatorda qismlar nomlari bilan tuzilishga aylantirishimiz mumkin.
Fayl nomi: src/main.rs
struct Kvadrat { kenglik: u32, balandlik: u32, } fn main() { let kvadrat1 = Kvadrat { kenglik: 30, balandlik: 50, }; println!( "To'rtburchakning maydoni {} kvadrat pikselga teng.", area(&kvadrat1) ); } fn area(kvadrat: &Kvadrat) -> u32 { kvadrat.kenglik * kvadrat.balandlik }
Bu yerda biz structni aniqladik va uni Kvadrat
deb nomladik. Jingalak qavslar ichida biz maydonlarni kenglik
va balandlik
sifatida belgiladik, ularning ikkalasi ham u32
turiga ega. Keyin, main
da biz Kvadrat
ning ma'lum bir misolini yaratdik, uning kengligi 30
va balandligi 50
.
Bizning area
funksiyamiz endi biz kvadrat
deb nomlagan bitta parametr bilan aniqlanadi, uning turi Kvadrat
structi misolining o‘zgarmas borrowidir. 4-bobda aytib o'tilganidek, biz unga ownershiplik qilishdan ko'ra, structi borrow qilishni xohlaymiz. Shunday qilib, main
o'z ownershipini saqlab qoladi va kvadrat1
dan foydalanishni davom ettirishi mumkin, shuning uchun biz funktsiya signaturesida &
dan foydalanamiz va biz funktiyani chaqiramiz.
area
funksiyasi Kvadrat
misolining kenglik
va balandlik
maydonlariga kiradi (esda tutingki, borrow qilingan struct misolining maydonlariga kirish maydon qiymatlarini ko'chirmaydi, shuning uchun siz ko'pincha structlarning borrowlarini ko'rasiz). Endi area
funksiyasi signaturesi biz nimani nazarda tutayotganimizni aniq aytadi: Kvadrat
maydonini uning kenglik
va balandlik
maydonlaridan foydalanib hisoblang. Bu kenglik va balandlik bir-biri bilan bog'liqligini bildiradi va 0
va 1
qator indeks qiymatlarini ishlatishdan ko'ra, qiymatlarga tavsiflovchi nomlar beradi. Bu aniqlik uchun g'alaba.
Olingan Traitlar bilan foydali funksionallikni qo'shish
Dasturimizni debug qilish va uning barcha maydonlari uchun qiymatlarni ko'rish paytida Kvadrat
misolini chop etish foydali bo'lar edi. 5-11 ro'yxatda biz avvalgi boblarda foydalanganimizdek println!
makrosidan foydalanishga harakat qiladi. Biroq, bu ishlamaydi.
Fayl nomi: src/main.rs
struct Kvadrat {
kenglik: u32,
balandlik: u32,
}
fn main() {
let kvadrat1 = Kvadrat {
kenglik: 30,
balandlik: 50,
};
println!("kvadrat1 - {}", kvadrat1);
}
Ushbu kodni kompilyatsiya qilishda, biz ushbu asosiy xabar bilan xatoga duch kelamiz:
error[E0277]: `Rectangle` doesn't implement `std::fmt::Display`
println!
makrosi ko'plab formatlash turlarini amalga oshirishi mumkin va standart bo'yicha jingalak qavslar println!
ga Display
deb nomlanuvchi formatlashdan foydalanishni bildiradi: to'g'ridan-to'g'ri oxirgi foydalanuvchi iste'moli uchun mo'ljallangan chiqish. Biz hozirgacha ko'rgan primitiv turlar standart bo'yicha Display
ni qo'llaydi, chunki foydalanuvchiga 1
yoki boshqa primitiv turni ko'rsatishning faqat bitta usuli bor. Lekin structlar bilan println!
ning chiqishni formatlash metodi unchalik aniq emas, chunki koʻproq koʻrsatish imkoniyatlari mavjud: vergul qoʻyishni xohlaysizmi yoki yoʻqmi? Jingalak qavslarni chop qilmoqchimisiz? Barcha maydonlar ko'rsatilishi kerakmi? Ushbu noaniqlik tufayli Rust biz xohlagan narsani taxmin qilishga urinmaydi va structlarda println!
va {}
to'ldiruvchisi bilan foydalanish uchun Display
ning taqdim etilgan ilovasi yo'q.
Agar xatolarni o'qishda davom etsak, biz ushbu foydali eslatmani topamiz:
= help: the trait `std::fmt::Display` is not implemented for `Kvadrat`
= note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
Keling, sinab ko'raylik! println!
macro chaqiruvi endi println!("kvadrat1 bu {}", kvadrat1);
kabi ko'rinadi. :?
spetsifikatsiyasini jingalak qavslar ichiga qo'yish println!
biz Debug
deb nomlangan chiqish formatidan foydalanmoqchi ekanligimizni bildiradi. Debug
traiti bizga sturctni ishlab chiquvchilar uchun foydali bo'lgan tarzda chop etish imkonini beradi, shuning uchun biz kodimizni tuzatish paytida uning qiymatini ko'rishimiz mumkin.
Keling, ushbu o'zgarishlar bilan kodni kompilyatsiya qilaylik. Ehh! Biz hali ham xatoni olamiz:
error[E0277]: `Rectangle` doesn't implement `Debug`
Ammo yana, kompilyator bizga foydali eslatma beradi:
= help: the trait `Debug` is not implemented for `Rectangle`
= note: add `#[derive(Debug)]` to `Rectangle` or manually `impl Debug for Rectangle`
Rust debug ma'lumotlarini chop etish funksiyasini o'z ichiga oladi, lekin biz ushbu funksiyani structimiz uchun mavjud qilish uchun ochiqdan-ochiq rozi bo'lishimiz kerak.
Buni amalga oshirish uchun 5-12 ro'yxatda ko'rsatilganidek, struct ta'rifidan oldin #[derive(Debug)]
tashqi atributini qo'shamiz.
Fayl nomi: src/main.rs
#[derive(Debug)] struct Kvadrat { kenglik: u32, balandlik: u32, } fn main() { let kvadrat1 = Kvadrat { kenglik: 30, balandlik: 50, }; println!("kvadrat1 - {}", kvadrat1); }
Endi dasturni ishga tushirganimizda, biz hech qanday xatolikka yo'l qo'ymaymiz va biz quyidagi natijani ko'ramiz:
$ cargo run
Compiling rectangles v0.1.0 (file:///projects/rectangles)
Finished dev [unoptimized + debuginfo] target(s) in 0.48s
Running `target/debug/rectangles`
kvadrat1 - Kvadrat { kenglik: 30, balandlik: 50 }
Yaxshi! Bu eng yaxshi natija emas, lekin u ushbu misol uchun barcha maydonlarning qiymatlarini ko'rsatadi, bu disk raskadrovka paytida albatta yordam beradi. Kattaroq structlarga ega bo'lsak, o'qishni biroz osonlashtiradigan chiqishga ega bo'lish foydalidir; bunday hollarda println!
qatoridagi {:?}
o'rniga {:#?}
dan foydalanishimiz mumkin. Ushbu misolda {:#?}
uslubidan foydalanish quyidagi natijalarni beradi:
$ cargo run
Compiling rectangles v0.1.0 (file:///projects/rectangles)
Finished dev [unoptimized + debuginfo] target(s) in 0.48s
Running `target/debug/rectangles`
kvadrat1 - Kvadrat {
kenglik: 30,
balandlik: 50
}
Debug
formati yordamida qiymatni chop etishning yana bir usuli dbg!
macro Ifodaga egalik qiluvchi macro (mos referencelar oladigan println! dan farqli o'laroq) o'sha dbg!
qayerda fayl va satr raqamini chop etadi! macro murojati sizning kodingizda ushbu ifodaning natijaviy qiymati bilan birga sodir bo'ladi va qiymatga egalik huquqini qaytaradi.
Eslatma:
dbg!
makrosini chaqirish standart chiqish konsoli stremiga (stdout
) chop qiluvchiprintln!
dan farqli ravishda standart xato konsoli stremiga (stderr
) chop etadi. Biz 12-bobdagi ”Xato xabarlarini standart chiqish o‘rniga standart xatoga yozish” bo‘limidastderr
vastdout
haqida ko‘proq gaplashamiz.
Mana bizni kenglik
maydoniga tayinlanadigan qiymat, shuningdek kvadrat1
dagi butun structning qiymati qiziqtiradigan misol:
#[derive(Debug)] struct Kvadrat { kenglik: u32, balandlik: u32, } fn main() { let masshtab = 2; let kvadrat1 = Kvadrat { kenglik: dbg!(30 * masshtab), balandlik: 50, }; dbg!(&kvadrat1); }
Biz 30 * masshtab
iborasi atrofida dbg!
qo'yishimiz mumkin va dbg!
ifoda qiymatiga egalik huquqini qaytargani uchun, kenglik
maydoni bizda dbg!
chaqiruvi bo'lmagani kabi bir xil qiymatga ega bo'ladi. Biz dbg!
kvadrat1
ga egalik qilishini istamaymiz, shuning uchun keyingi chaqiruvda kvadrat1
ga referencedan foydalanamiz.
Ushbu misolning natijasi quyidagicha ko'rinadi:
$ cargo run
Compiling rectangles v0.1.0 (file:///projects/rectangles)
Finished dev [unoptimized + debuginfo] target(s) in 0.61s
Running `target/debug/rectangles`
[src/main.rs:10] 30 * masshtab = 60
[src/main.rs:14] &kvadrat1 = Kvadrat {
kenglik: 30,
balandlik: 50
}
Biz birinchi debuggingda src/main.rs ning 10-qatoridan kelganini ko'rishimiz mumkin, bu erda biz 30 * masshtab ifodani debugging qilamiz va uning natijaviy qiymati 60
(butun sonlar uchun Debug
formati faqat ularning qiymatini chop etish uchun ishlatiladi). src/main.rs ning 14-qatoridagi dbg!
chaqiruvi &kvadrat1
qiymatini chiqaradi, bu Kvadrat
structidir. Ushbu chiqishda Kvadrat
turidagi chiroyli Debug
formatlash qo'llaniladi. dbg!
makrosi sizning kodingiz nima qilayotganini aniqlashga harakat qilayotganingizda juda foydali bo'lishi mumkin!
Rust Debug
traitiga qo‘shimcha ravishda derive
atributi bilan foydalanishimiz uchun bir qancha taritlarni taqdim etdi, ular bizning odatiy turlarimizga foydali xatti-harakatlar qo‘shishi mumkin.Ushbu traitlar va ularning xatti-harakatlari C ilovasida keltirilgan. Biz 10-bobda ushbu traittlarni odatiy xatti-harakatlar bilan qanday implement qilishni, shuningdek, o'z traitlaringizni qanday yaratishni ko'rib chiqamiz.Bundan tashqari, 'derive' dan boshqa ko'plab atributlar mavjud; qo'shimcha ma'lumot olish uchun Rust Referencening "Atributlar" bo'limiga qarang.
Bizning area
funksiyamiz juda aniq: u faqat to'rtburchaklar maydonini hisoblaydi. Ushbu xatti-harakatni Kvadrat
structimiz bilan yaqinroq bog'lash foydali bo'ladi, chunki u boshqa turlar bilan ishlamaydi. Keling, ushbu kodni qanday qilib qayta ishlashni davom ettirishimiz mumkinligini ko'rib chiqaylik, bu area
funksiyasini Kvadrat
turida aniqlangan area
metod ga aylantiradi.