Cél és előfeltétel

Egy név hosszának megméréséhez nincs szükség arra, hogy a mérő függvény a teljes szöveg gazdája legyen. Ha pedig egy címke végére jelet akarsz írni, akkor ideiglenes módosítási jogra van szükséged. A kölcsönzés ezt a két helyzetet teszi láthatóvá a típusokban. A tulajdonos továbbra is megmarad, de a hozzáférésnek szabályai vannak.

Előfeltétel az értékmásolás és move. A példák külön edition 2024 programok, és nem igényelnek bemenetet. A hangsúly nem a memóriacímek kinyomtatásán, hanem a hívás előtt és után használható értékeken van. Rajzold fel, melyik sor olvas, melyik módosít, és melyik sor után nincs már szükség egy korábbi referenciára.

A megosztott referencia olvasást kér

A megosztott referencia az és jellel készül, típusa pedig ugyanilyen jelölést kap. Egy String megosztott referenciáját váró függvény nem veszi át a String tulajdonát. A hívás után a tulajdonos változó továbbra is használható. Több ilyen olvasási hozzáférés létezhet egyszerre, amíg az adatot közben nem módosítjuk olyan módon, amely az élő kölcsönzésekkel összeütközik.

A referencia nem az eredeti érték teljes klónja. Amikor csak olvasni szeretnél, ezért gyakran jobb szerződés, mint egy felesleges String másolat. A paraméter típusa az olvasónak is üzen: a függvény nem kapott módosítási hozzáférést. Ez a lecke String referenciával mutatja meg az alapötletet; a következőben az általánosabb szövegszelet-paramétert is használod.

A módosítható referencia kizárólagos hozzáférést kér

A módosítható kölcsönzés jelölése az és jel után álló mut. Ahhoz, hogy ilyet adj egy változóból, a változónak is módosíthatónak kell lennie. A referencia lehetővé teszi a tartalom átírását anélkül, hogy a tulajdont végleg átadnád. A függvény visszatérhet például üres egységértékkel: a művelet eredménye ilyenkor az eredeti adatban látszik.

Ugyanahhoz az értékhez nem lehet egyszerre összeütköző, aktívan használt kizárólagos hozzáféréseket tartani. A szabály nem önkényes tiltás: megakadályozza, hogy egy olvasó közben megváltozó vagy érvénytelenné váló adatot lásson. Ha két külön módosítást szeretnél, rendszerint egymás után végezheted el őket. Nem kell minden ilyen művelethez új másolatot létrehozni.

A használat végét figyeld, ne csak a kapcsos zárójelet

Egy referencia kölcsönzése sok esetben az utolsó használata után már nem akadályoz további műveletet. A fordító nem feltétlenül ragaszkodik a teljes külső blokk végéhez. Ha egy szöveg olvasó referenciáját még később is használod, közben nem módosíthatod ellentmondó módon a forrást. Ha az utolsó olvasás már megtörtént, a későbbi módosítás megengedett lehet.

Kezdőként a rövid, egymás utáni műveletek a legáttekinthetőbbek. Előbb kérdezd le és ellenőrizd az adatot, aztán módosítsd, majd újra olvasd. A fordító hibaüzenetében keresd az első kölcsönzést és a későbbi használatot; a problémát gyakran az a későbbi sor tartja életben, amelyet első pillantásra észre sem veszel. Egy új blokk néha segíthet, de előbb a szükséges használati sorrendet tisztázd.

1. feladat: két olvasó ugyanarról a szövegről

Hozz létre egy Budapest szöveget, majd készíts belőle két megosztott referenciát. Mindkettővel ellenőrizd ugyanazt a tartalmat, és vizsgáld meg a bájthosszt is. A példában minden betű egybájtos, ezért a nyolcas hossz egyszerűen követhető. A hívások után a tulajdonost is használd, ezzel ellenőrizve, hogy nem történt move.

Megoldás
fn bajthossz(szoveg: &String) -> usize { szoveg.len() }
fn main() {
    let varos = String::from("Budapest");
    let elso = &varos;
    let masodik = &varos;
    assert_eq!(elso, "Budapest");
    assert_eq!(masodik, "Budapest");
    assert_eq!(bajthossz(&varos), 8);
    assert_eq!(varos, "Budapest");
}

A hossz itt bájtban értendő, nem általános emberi karakterszám. Ékezetes szövegnél a két mennyiség eltérhet, ezért az elnevezés tudatosan pontos.

2. feladat: két egymást követő módosítás

Írj függvényt, amely módosítható String referenciát fogad, és egy pontot fűz a végére. Hívd meg kétszer ugyanazzal a tulajdonos változóval, egymás után. Ellenőrizd a kétpontos végződést és az üres bemenet egyszeri módosítását is. Ne adj vissza másolatot: a változás az átadott szövegben történjen.

Megoldás
fn pontoz(szoveg: &mut String) { szoveg.push('.'); }
fn main() {
    let mut nev = String::from("Napló");
    pontoz(&mut nev);
    pontoz(&mut nev);
    assert_eq!(nev, "Napló..");
    let mut ures = String::new();
    pontoz(&mut ures);
    assert_eq!(ures, ".");
}

A két hívás nem tart egyszerre két aktív, egymással versengő módosító referenciát. Az első hívás kölcsönzése befejeződik, mielőtt a második megkezdődik.

3. feladat: olvasás után módosítás

Készíts olvasó referenciát egy rövid címkéből, és azzal ellenőrizd a kezdő tartalmat. Csak ezután fűzz új részt a tulajdonoshoz, majd az eredeti változón ellenőrizd a teljes eredményt. Az olvasó referenciát a módosítás után már ne használd. Figyeld meg, hogy ehhez nem szükséges kézzel felszabadítani vagy nullára állítani a referenciát.

Megoldás
fn main() {
    let mut cimke = String::from("alap");
    let olvaso = &cimke;
    assert_eq!(olvaso, "alap");
    cimke.push_str("-uj");
    assert_eq!(cimke, "alap-uj");
    let uj_olvaso = &cimke;
    assert_eq!(uj_olvaso.len(), 7);
}

Az utolsó olvasó referencia már a módosítás utáni tartalmat vizsgálja. Nem ugyanannak a korábbi nézetnek a korlátlan, változásokon átívelő használatát kérjük a fordítótól.

Elfogadási feltételek

  • A két megosztott referencia ugyanazt a tartalmat olvassa, és a tulajdonos a végén is használható.
  • A két egymást követő módosító hívás két pontot fűz hozzá, az üres szöveg pedig egy ponttá válik.
  • A módosítás az első olvasó utolsó használata után történik; az új ellenőrzés a friss tartalmat látja.

Továbblépés

A hozzáférési jog már a paramétertípusból látszik. A szeletek és kölcsönzött nézetek leckében nem az egész tárolót, hanem annak egy részét adod át, továbbra is másolás nélkül.