Historia pytania
W Rust typy często inkapsulują inne wartości (na przykład, Box, Rc), a programiści pragnęli przezroczystego dostępu do zagnieżdżonych wartości, jak w przypadku wskaźników w C++. Dodano trait Deref (i DerefMut) oraz specjalną logikę auto-deref dla wygodnego dostępu przez metody i operatory.
Problem
Zamieszanie w kwestii tego, kiedy i jak automatycznie rozwijają się wskaźniki i wskaźniki inteligentne, prowadzi do błędów kompilacji, nieoczekiwanego zachowania metod, a czasami nawet do nieoptymalnego kodu z powodu zbędnych kopii lub borrowów.
Rozwiązanie
Trait Deref pozwala opisać swoją regułę dereferencjonowania (na przykład, Box<T> zachowuje się jak T dzięki implementacji Deref). System auto-deref stara się "rozwinąć" odniesienia do typów Deref podczas dostępu do metod i operatorów. To pozwala pisać:
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
Kluczowe cechy:
*, a także przy dopasowywaniu typów referencyjnychCzy auto-deref występuje zawsze, gdy "spodziewa się" tego programista?
Nie: deref coercion występuje tylko przy wywołaniu metod lub gdy wymagane jest odniesienie do innego typu, określonego w sygnaturze przez Deref Target. Nie działa we wszystkich wyrażeniach, na przykład przy pattern matching.
Czy deref coercion może złamać zasady borrowing i własności?
Nie. Deref coercion całkowicie przestrzega zasad borrowing — niemożliwe jest uzyskanie dwóch mutable-referencji przez Deref, naruszając bezpieczeństwo własności w Rust.
Czy auto-deref jest możliwy z &T do &U, jeśli T: Deref<Target=U> i oba typy nie są wyraźnie powiązane?
Tak, przy wywołaniu funkcji oczekującej &U, przekazanie &T, gdzie T implementuje Deref<Target=U>, spowoduje automatyczną konwersję.
Przykład kodu:
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
Programista zaimplementował swój wskaźnik inteligentny, ale nie dodał implementacji Deref, oczekując, że jego typ będzie działał jak zwykła wartość.
Zalety:
Typ się kompiluje i można jawnie uzyskać dostęp do zagnieżdżonej wartości.
Wady:
Przestaje działać auto-deref przy wywołaniu metod, utrudniając korzystanie z funkcji stdlib i bibliotek zewnętrznych.
Własna struktura-opakowanie implementuje Deref i DerefMut analogicznie do Box, bezproblemowa integracja ze wszystkimi standardowymi funkcjami.
Zalety:
Wygoda, przezroczysta praca większości funkcji języka, czystość i subtelna integracja z innym kodem.
Wady:
Ryzyko skomplikowania interfejsu Deref, jeśli typ docelowy nie jest oczywisty.