Achtergrond
In Rust encapsuleren types vaak andere waarden (zoals Box, Rc), en ontwikkelaars wilden transparante toegang tot geneste waarden, zoals met pointers in C++. De trait Deref (en DerefMut) werd toegevoegd samen met speciale auto-deref logica voor gemakkelijker toegang via methoden en operators.
Probleem
Verwarring over wanneer en hoe pointers en smart pointers automatisch worden gedereferencet, leidt tot compileerfouten, onverwacht gedrag van methoden, en soms zelfs tot suboptimale code door onnodige kopieën of borrow's.
Oplossing
De trait Deref stelt je in staat om je eigen dereferentieregels te beschrijven (bijvoorbeeld, Box<T> gedraagt zich als T dankzij de implementatie van Deref). Het auto-deref systeem probeert verwijzingen naar Deref-types te 'dereferencen' bij het aanroepen van methoden en operators. Dit stelt je in staat om te schrijven:
use std::rc::Rc; fn print_len(s: &str) { println!("{}", s.len()); } let s: Rc<String> = Rc::new("hello".into()); print_len(&s); // auto-deref: &Rc<String> -> &String -> &str
Belangrijke kenmerken:
*, evenals bij het matchen van verwijzings-typesGebeurd auto-deref altijd wanneer de ontwikkelaar dat "verwacht"?
Nee: deref coercion gebeurt alleen bij het aanroepen van methoden of wanneer een verwijzing naar een ander type vereist is, zoals gespecificeerd in de handtekening via Deref Target. Het werkt niet in alle expressies, bijvoorbeeld bij pattern matching.
Kan deref coercion de regels van borrowing en eigendom breken?
Nee. Deref coercion houdt zich volledig aan de regels van borrowing — het is onmogelijk om twee mutable-referenties te krijgen via Deref, wat de veiligheid van eigendom in Rust schendt.
Is auto-deref mogelijk van &T naar &U, als T: Deref<Target=U> en beide types niet expliciet verwant zijn?
Ja, bij het aanroepen van een functie die &U verwacht, zal het doorgeven van &T, waar T Deref<Target=U> implementeert, leiden tot automatische conversie.
Voorbeeldcode:
struct Wrapper(String); impl std::ops::Deref for Wrapper { type Target = String; fn deref(&self) -> &Self::Target { &self.0 } } fn takes_str(s: &str) {} let w = Wrapper("mytext".into()); takes_str(&w); // auto-deref: &Wrapper -> &String -> &str
Een ontwikkelaar implementeerde zijn eigen smart pointer, maar voegde geen implementatie van Deref toe, in de veronderstelling dat zijn type zich als een gewone waarde zou gedragen.
Voordelen:
Het type compileert en men kan expliciet naar de geneste waarde verwijzen.
Nadelen:
Auto-deref werkt niet meer bij het aanroepen van methoden, wat ongemak met zich meebrengt bij het gebruik van stdlib functies en externe bibliotheken.
Zijn wrapper-structuur implementeert Deref en DerefMut net als Box, naadloze integratie met alle standaardfuncties.
Voordelen:
Gemak, transparante werking van de meeste taal functies, netheid en zachte integratie met de rest van de code.
Nadelen:
Risico om de Deref-interface te compliceren als het target-type niet duidelijk is.