Wyobraź sobie źródło jakiegoś zdarzenia, które generuje zdarzenia reprezentowane jako wyliczenie. Oczywiście, dla uzyskania najlepszej wydajności, producent ten jest zero-copy, czyli zwraca referencje do wewnętrznych buforów:Dlaczego cecha Pożycz wymaga, aby pożyczony typ był referencyjny?
enum Variant<'a> {
Nothing,
SomeInt(u64),
SomeBytes(&'a [u8])
}
impl Producer {
fn next(&'a mut self) -> Variant<'a> { ... }
}
Jest to całkowicie w porządku dla konsumentów, które nie wymagają uprzedzona lub wycofywania, ale czasami jest trzeba zapisać pewną sekwencję zdarzeń. Tak więc, nasz Variant
typ staje się ogólna:
enum Variant<BytesT> {
Nothing,
SomeInt(u64),
SomeBytes(BytesT)
}
type OwnedVariant = Variant<Vec<u8>>;
type BorrowedVariant<'a> = Variant<&'a [u8]>;
Tutaj kończy się z dwóch typów z „właściciela odniesienia” związku, który jest podobny do pary Vec<T>
- &[T]
, String
- &str
. Dokumenty sugerują wbudowanych cech Borrow
i ToOwned
które dostarczają tylko to, co jest wymagane z wyjątkiem subtelnych niuansów:
trait Borrow<Borrowed: ?Sized> {
fn borrow(&self) -> &Borrowed;
// this: -----------^
}
pub trait ToOwned {
type Owned: Borrow<Self>;
fn to_owned(&self) -> Self::Owned;
}
Wynik borrow
ma obowiązek być odniesienie do czegoś, co BorrowedVariant<'a>
nie jest oczywisty. Usunięcie tego wymogu rozwiązuje ten problem (tutaj nazwy są poprzedzone ALT podkreślić fakt, to jest alternatywny interfejs)
trait AltBorrow<'a, AltBorrowed> {
fn alt_borrow(&'a self) -> AltBorrowed;
}
trait AltToOwned<'a> {
type AltOwned: AltBorrow<'a, Self>;
fn alt_to_owned(&'a self) -> Self::AltOwned;
}
Cecha ta może być następnie realizowana przez standardowe typy np Vec
:
impl<'a, T> AltBorrow<'a, &'a [T]> for Vec<T> {
fn alt_borrow(&'a self) -> &'a [T] {
self.as_slice()
}
}
impl<'a, T> AltToOwned<'a> for &'a [T]
where T: Clone
{
type AltOwned = Vec<T>;
fn alt_to_owned(&'a self) -> Vec<T> {
self.to_vec()
}
}
Jak również dla Variant
wyliczenia w pytaniu:
impl<'a> AltBorrow<'a, BorrowedVariant<'a>> for OwnedVariant {
fn alt_borrow(&'a self) -> BorrowedVariant<'a> {
match self {
&Variant::Nothing => Variant::Nothing,
&Variant::SomeInt(value) => Variant::SomeInt(value),
&Variant::SomeBytes(ref value) => Variant::SomeBytes(value.alt_borrow()),
}
}
}
impl<'a> AltToOwned<'a> for BorrowedVariant<'a> {
type AltOwned = OwnedVariant;
fn alt_to_owned(&'a self) -> OwnedVariant {
match self {
&Variant::Nothing => Variant::Nothing,
&Variant::SomeInt(value) => Variant::SomeInt(value),
&Variant::SomeBytes(value) => Variant::SomeBytes(value.alt_to_owned()),
}
}
}
Wreszcie pytania:
- jestem nadużywania oryginalny
Borrow
/ToOwned
koncepcję? Czy powinienem użyć czegoś innego, aby to osiągnąć? - Jeśli nie, to jakie są powody, dla których preferowany był obecny mniej ogólny interfejs z
std::borrow
?