Metod Sintaksisi

Metodlar funksiyalarga oʻxshaydi: biz ularni fn kalit soʻzi va nomi bilan eʼlon qilamiz, ular parametrlari va qaytish qiymatiga ega boʻlishi mumkin va ular boshqa joydan metod chaqirilganda ishga tushadigan kodni oʻz ichiga oladi. Funktsiyalardan farqli o'laroq, metodlar struct (yoki biz mos ravishda 6-bob va 17-bobda ko'rib chiqiladigan enum yoki trait obyekti) kontekstida aniqlanadi va ularning birinchi parametri har doim self dir metod chaqirilayotgan structning namunasini ifodalaydi.

Metodlarni aniqlash

Parametr sifatida Kvadrat misoliga ega bo‘lgan area funksiyasini o‘zgartiramiz va uning o‘rniga 5-13 ro‘yxatda ko'rsatilganidek, Kvadrat structida belgilangan area metodini yaratamiz.

Fayl nomi: src/main.rs

#[derive(Debug)]
struct Kvadrat {
    kenglik: u32,
    balandlik: u32,
}

impl Kvadrat {
    fn area(&self) -> u32 {
        self.kenglik * self.balandlik
    }
}

fn main() {
    let kvadrat1 = Kvadrat {
        kenglik: 30,
        balandlik: 50,
    };

    println!(
        "To'rtburchakning maydoni {} kvadrat pikselga teng.",
        kvadrat1.area()
    );
}

Ro'yxat 5-13: Kvadrat structida area metodini aniqlash

Kvadrat kontekstida funksiyani aniqlash uchun Kvadrat uchun impl (implementation) blokini ishga tushiramiz. Ushbu impl blokidagi hamma narsa Kvadrat turi bilan bog'lanadi. Keyin biz area funksiyasini impl jingalak qavslar ichida harakatlantiramiz va birinchi (va bu holda, faqat) parametrni signatureda va tananing hamma joyida self o‘zgartiramiz. main da, biz area funksiyasini chaqirib, argument sifatida kvadrat1 ni topshirgan bo‘lsak, o‘rniga Kvadrat misolida area metodini chaqirish uchun metod sintaksisi dan foydalanishimiz mumkin. Metod sintaksisi misoldan keyin keladi: biz nuqta qo'shamiz, undan keyin metod nomi, qavslar va har qanday argumentlar qo'shiladi.

area uchun signatureda kvadrat: &Kvadrat o‘rniga &self dan foydalanamiz. &self aslida self: &Self ning qisqartmasi. impl blokida Self turi impl bloki uchun bo'lgan turdagi taxallusdir. Metodlar birinchi parametri uchun Self turidagi self deb nomlangan parametrga ega bo'lishi kerak, shuning uchun Rust birinchi parametr joyida faqat self nomi bilan qisqartirish imkonini beradi. Esda tutingki, biz hali ham kvadrat: &Kvadrat da qilganimizdek, bu metod Self misolini olishini koʻrsatish uchun Self stenografiyasi oldida & dan foydalanishimiz kerak. Boshqa har qanday parametr singari, metodlar self egallashi, o'zgarmas self borrow qilishi mumkin, xuddi biz bu yerda qilganimizdek yoki o'zgaruvchan selfni borrow qilishi mumkin.

Biz bu yerda funksiya versiyasida &Kvadrat dan foydalanganimiz uchun xuddi shu sababga ko‘ra &self tanladik: biz ownershiplik qilishni istamaymiz va faqat structdagi ma’lumotlarni o‘qishni istaymiz, unga yozishni emas. Agar biz ushbu metodning bir qismi sifatida chaqirgan misolni o'zgartirmoqchi bo'lsak, birinchi parametr sifatida &mut self dan foydalanamiz. Birinchi parametr sifatida faqat selfni ishlatib, misolga ownershiplik qiladigan metod kamdan-kam uchraydi; bu metod odatda selfni boshqa narsaga aylantirganda va siz murojat qiluvchiga transformatsiyadan keyin asl nusxadan foydalanishiga yo'l qo'ymaslikni istasangiz ishlatiladi.

Funktsiyalar o'rniga metodlardan foydalanishning asosiy sababi, har bir metod signaturesida selfturini takrorlashning hojati bo'lmagan metod sintaksisidan tashqari, kodni tashkil qilishdir. Biz kelajakdagi kod foydalanuvchilarini biz taqdim etayotgan kutubxonaning turli joylarida Kvadrat imkoniyatlarini izlashga majburlashdan ko‘ra, biz tur namunasi bilan qila oladigan barcha narsalarni bitta impl blokiga joylashtirdik.

E'tibor bering, biz metodga structning maydonlaridan biri bilan bir xil nom berishni tanlashimiz mumkin. Misol uchun, biz Kvadrat da kenglik deb nomlangan metodni belgilashimiz mumkin:

Fayl nomi: src/main.rs

#[derive(Debug)]
struct Kvadrat {
    kenglik: u32,
    balandlik: u32,
}

impl Kvadrat {
    fn kenglik(&self) -> bool {
        self.kenglik > 0
    }
}

fn main() {
    let kvadrat1 = Kvadrat {
        kenglik: 30,
        balandlik: 50,
    };

    if kvadrat1.kenglik() {
        println!("To'rtburchakning kengligi nolga teng bo'lmagan; bu {}", kvadrat1.kenglik);
    }
}

Bu yerda, agar misolning kenglik maydonidagi qiymat 0 dan katta bo‘lsa, kenglik metodi true qiymatini qaytaradi, agar qiymat 0 bo'lsa, false bo‘lishini tanlaymiz: biz bir xil nomdagi metod ichidagi maydonni istalgan maqsadda ishlatishimiz mumkin. main da, biz qavslar bilan kvadrat1.kenglik ga amal qilsak, Rust kenglik metodini nazarda tutayotganimizni biladi. Qavslardan foydalanmasak, Rust kenglik maydonini nazarda tutayotganimizni biladi.

Ko'pincha, lekin har doim emas, biz metodga maydon bilan bir xil nom berganimizda, biz u faqat maydondagi qiymatni qaytarishini va boshqa hech narsa qilmasligini xohlaymiz. Shunga o'xshash metodlar getters deb ataladi va Rust ularni boshqa tillarda bo'lgani kabi tizim maydonlari uchun avtomatik ravishda amalga oshirmaydi. Getterslar foydalidir, chunki siz maydonni shaxsiy(private), lekin metodni hammaga ochiq(public) qilib qo'yishingiz mumkin va shu tariqa ushbu maydonga umumiy API ning bir qismi sifatida faqat o'qish uchun ruxsatni yoqishingiz mumkin. Biz 7-bobda public va private nima ekanligini va qanday qilib maydon yoki metodni public yoki private deb belgilashni muhokama qilamiz.

-> operatori qayerda ishlatiladi?

C va C++ tillarida metodlarni chaqirish uchun ikki xil operator qo'llaniladi: obyektdagi metodni to'g'ridan-to'g'ri chaqirayotgan bo'lsangiz . va agar siz ko'rsatgichdagi metodni obyektga chaqirayotgan bo'lsangiz va avval ko'rsatgichni yo'qotishingiz kerak bo'lsa -> dan foydalanasiz. Boshqacha qilib aytganda, agar object havola bo'lsa, u holda object->something() va (*object).something() metodi chaqiruvlari bir xil bo'ladi.

Rust -> operatoriga ekvivalentga ega emas; Buning o'rniga Rustda avtomatik reference va dereferencing deb nomlangan xususiyat mavjud. Metodni chaqirish Rustda bunday xatti-harakatlarga ega bo'lgan kam sonli joylardan biridir.

Bu shunday ishlaydi: object.something() bilan metodni chaqirganingizda, Rust avtomatik ravishda &, &mut yoki * ni qo'shadi, shuning uchun object metod signaturega mos keladi. Boshqacha qilib aytganda, quyidagilar bir xil:

#![allow(unused)]
fn main() {
#[derive(Debug,Copy,Clone)]
struct Point {
    x: f64,
    y: f64,
}

impl Point {
   fn masofa(&self, other: &Point) -> f64 {
       let x_kvadrat = f64::powi(other.x - self.x, 2);
       let y_kvadrat = f64::powi(other.y - self.y, 2);

       f64::sqrt(x_kvadrat + y_kvadrat)
   }
}
let p1 = Point { x: 0.0, y: 0.0 };
let p2 = Point { x: 5.0, y: 6.5 };
p1.masofa(&p2);
(&p1).masofa(&p2);
}

Birinchisi ancha toza ko'rinadi. Ushbu avtomatik reference qilish harakati, metodlar aniq qabul qiluvchiga ega bo'lganligi sababli ishlaydi - self turi. Qabul qiluvchi va metod nomini hisobga olgan holda, Rust ma'lum bir holatda kod nima qilayotganini aniq aniqlashi mumkin: o'qish (&self), o'zgartirish (&mut self) yoki iste'mol qilish (self). Rust metodi qabul qiluvchilar uchun borrow qilishni yashirin qilib qo'yganligi amalda ownershipni ergonomik qilishning katta qismidir.

Ko'proq parametrlarga ega metodlar

Kvadrat structida ikkinchi metodni implement qilish orqali metodlardan foydalanishni mashq qilaylik. Bu safar biz Kvadrat misoli Kvadrat ning boshqa nusxasini olishini va agar ikkinchi Kvadrat to'liq o'ziga (birinchi Kvadrat) sig'ishi mumkin bo'lsa, true qiymatini qaytarishini istaymiz; aks holda u falseni qaytarishi kerak. Ya'ni, ushlab_tur metodini aniqlaganimizdan so'ng, biz 5-14 ro'yxatda ko'rsatilgan dasturni yozish imkoniyatiga ega bo'lishni xohlaymiz.

Fayl nomi: src/main.rs

fn main() {
    let kvadrat1 = Kvadrat {
        kenglik: 30,
        balandlik: 50,
    };
    let kvadrat2 = Kvadrat {
        kenglik: 10,
        balandlik: 40,
    };
    let kvadrat3 = Kvadrat {
        kenglik: 60,
        balandlik: 45,
    };

    println!("kvadrat1 kvadrat2ni ushlab turadimi? {}", kvadrat1.ushlab_tur(&kvadrat2));
    println!("kvadrat1 kvadrat3ni ushlab turadimi? {}", kvadrat1.ushlab_tur(&kvadrat3));
}

Ro'yxat 5-14: Hali yozilmagan ushlab_tur dan foydalanish metodi

Kutilgan natija quyidagicha ko‘rinadi, chunki kvadrat2 ning ikkala o‘lchami kvadrat1 o‘lchamidan kichikroq, lekin kvadrat3 kvadrat1 dan kengroq:

kvadrat1 kvadrat2ni ushlab turadimi? true
kvadrat1 kvadrat3ni ushlab turadimi? false

Biz metodni aniqlamoqchi ekanligimizni bilamiz, shuning uchun u impl Kvadrat blokida bo'ladi. Metod nomi ushlab_tur bo'ladi va u parametr sifatida boshqa Kvadrat ning o'zgarmas borrowini oladi. Parametrning turi qanday bo'lishini metodni chaqiruvchi kodga qarab aniqlashimiz mumkin: kvadrat1.ushlab_tur(&kvadrat2) &kvadrat2 da o'tadi, bu kvadrat2 ga o'zgarmas borrow, Kvadrat misoli. Bu mantiqqa to'g'ri keladi, chunki biz faqat kvadrat2 ni o'qishimiz kerak (yozishdan ko'ra, bu bizga o'zgaruvchan borrow kerak degan ma'noni anglatadi), va biz main kvadrat2 ownershipligini saqlab qolishini istaymiz, shuning uchun ushlab_tur metodini chaqirganimizdan keyin uni qayta ishlatishimiz mumkin. ushlab_tur ning return qiymati mantiqiy qiymat bo'ladi va implement self ning kengligi va balandligi mos ravishda boshqa Kvadrat ning kengligi va balandligidan katta ekanligini tekshiradi. Keling, 5-15 ro'yxatda ko'rsatilgan 5-13 ro'yxatdagi impl blokiga yangi ushlab_tur metodini qo'shamiz.

Fayl nomi: src/main.rs

#[derive(Debug)]
struct Kvadrat {
    kenglik: u32,
    balandlik: u32,
}

impl Kvadrat {
    fn area(&self) -> u32 {
        self.kenglik * self.balandlik
    }

    fn ushlab_tur(&self, other: &Kvadrat) -> bool {
        self.kenglik > other.kenglik && self.balandlik > other.balandlik
    }
}

fn main() {
    let kvadrat1 = Kvadrat {
        kenglik: 30,
        balandlik: 50,
    };
    let kvadrat2 = Kvadrat {
        kenglik: 10,
        balandlik: 40,
    };
    let kvadrat3 = Kvadrat {
        kenglik: 60,
        balandlik: 45,
    };

    println!("kvadrat1 kvadrat2ni ushlab turadimi? {}", kvadrat1.ushlab_tur(&kvadrat2));
    println!("kvadrat1 kvadrat3ni ushlab turadimi? {}", kvadrat1.ushlab_tur(&kvadrat3));
}

Ro'yxat 5-15: Parametr sifatida boshqa Kvadrat misolini oladigan ushlab_tur metodini Kvadratda qo'llash

Ushbu kodni 5-14 ro'yxatdagi main funksiya bilan ishga tushirganimizda, biz kerakli natijani olamiz. Metodlar biz signaturega self parametridan keyin qo'shadigan bir nechta parametrlarni olishi mumkin va bu parametrlar funksiyalardagi parametrlar kabi ishlaydi.

Associate Funksiyalar

Associate Funksiyalar (Bog'langan Funktsiyalar).impl blokida aniqlangan barcha funksiyalar associated funksiyalar deb ataladi, chunki ular impl nomi bilan atalgan tur bilan bog‘langan. Biz birinchi parametr sifatida self ega bo'lmagan associated funksiyalarni belgilashimiz mumkin (va shuning uchun metodlar emas), chunki ular bilan ishlash uchun turdagi namuna kerak emas. Biz allaqachon shunday funksiyadan foydalanganmiz: String turida aniqlangan String::from funksiyasi.

Metod bo'lmagan associated funktsiyalar ko'pincha structning yangi nusxasini qaytaradigan konstruktorlar uchun ishlatiladi. Ular ko'pincha new deb ataladi, ammo new maxsus nom emas va tilga kiritilmagan. Masalan, biz bir o‘lchamli parametrga ega bo‘lgan kvadrat nomli associated funksiyani taqdim etishimiz va undan kenglik va balandlik sifatida foydalanishimiz mumkin, bu esa bir xil qiymatni ikki marta belgilashdan ko‘ra Kvadrat kvadratini yaratishni osonlashtiradi. :

Fayl nomi: src/main.rs

#[derive(Debug)]
struct Kvadrat {
    kenglik: u32,
    balandlik: u32,
}

impl Kvadrat {
    fn kvadrat(size: u32) -> Self {
        Self {
            kenglik: size,
            balandlik: size,
        }
    }
}

fn main() {
    let kv = Kvadrat::kvadrat(3);
}

Return turidagi va funksiya tanasidagi Self kalit so'zlari impl kalit so'zidan keyin paydo bo'ladigan turning taxalluslari bo'lib, bu holda Kvadrat bo'ladi.We’ll discuss modules in Chapter 7.

Ushbu associated funktsiyani chaqirish uchun biz struct nomi bilan :: sintaksisidan foydalanamiz; let kv = Kvadrat::kvadrat(3); misol bo'la oladi. Bu funksiya struct tomonidan nom maydoniga ega: :: sintaksisi ham associated funksiyalar, ham modullar tomonidan yaratilgan nomlar bo'shliqlari uchun ishlatiladi. Biz modullarni 7-bobda muhokama qilamiz.

Bir nechta impl bloklari

Har bir structga bir nechta impl bloklari ruxsat etiladi. Masalan, 5-15 ro'yxati 5-16 ro'yxatida ko'rsatilgan kodga ekvivalent bo'lib, har bir metod o'zining impl blokiga ega yani har bir metod o'z impl blokida.

#[derive(Debug)]
struct Kvadrat {
    kenglik: u32,
    balandlik: u32,
}

impl Kvadrat {
    fn area(&self) -> u32 {
        self.kenglik * self.balandlik
    }
}

impl Kvadrat {
    fn ushlab_tur(&self, other: &Kvadrat) -> bool {
        self.kenglik > other.kenglik && self.balandlik > other.balandlik
    }
}

fn main() {
    let kvadrat1 = Kvadrat {
        kenglik: 30,
        balandlik: 50,
    };
    let kvadrat2 = Kvadrat {
        kenglik: 10,
        balandlik: 40,
    };
    let kvadrat3 = Kvadrat {
        kenglik: 60,
        balandlik: 45,
    };

    println!("kvadrat1 kvadrat2ni ushlab turadimi? {}", kvadrat1.ushlab_tur(&kvadrat2));
    println!("kvadrat1 kvadrat3ni ushlab turadimi? {}", kvadrat1.ushlab_tur(&kvadrat3));
}

Ro'yxat 5-16: Bir nechta impl bloklari yordamida 5-15 ro'yxatini qayta yozish

Bu metodlarni bir nechta impl bloklariga ajratish uchun hech qanday sabab yo'q, lekin bu to'g'ri sintaksis. Biz 10-bobda bir nechta impl bloklari foydali bo'lgan holatni ko'rib chiqamiz, bu yerda biz umumiy turlar va taritlarni muhokama qilamiz.

Xulosa

Structlar sizning domeningiz uchun mazmunli bo'lgan maxsus turlarni yaratishga imkon beradi. Structlardan foydalanib, siz bog'langan ma'lumotlar qismlarini bir-biriga bog'lab qo'yishingiz va kodingizni aniq qilish uchun har bir qismga nom berishingiz mumkin. impl bloklarida siz o'zingizning turingiz bilan bog'liq bo'lgan funksiyalarni belgilashingiz mumkin va metodlar - bu sizning structlaringiz misollarining xatti-harakatlarini belgilashga imkon beruvchi associated funksiyaning bir turi.

Ammo structlar maxsus turlarni yaratishning yagona usuli emas: toolboxga boshqa toolni qo'shish uchun Rust enum xususiyatiga murojaat qilaylik.