Immaginate qualche fonte di eventi, che produce eventi rappresentati come enum. Naturalmente, per la migliore efficienza, questo produttore è pari a zero-copy, cioè ritorna riferimenti ai suoi buffer interni:Perché la caratteristica del prestito richiede che il tipo preso in prestito sia un riferimento?
enum Variant<'a> {
Nothing,
SomeInt(u64),
SomeBytes(&'a [u8])
}
impl Producer {
fn next(&'a mut self) -> Variant<'a> { ... }
}
Questo è perfettamente bene per i consumatori che non richiedono lookahead o backtracking, ma a volte c'è un è necessario salvare una sequenza di eventi. Così, il nostro tipo Variant
diventa un generico:
enum Variant<BytesT> {
Nothing,
SomeInt(u64),
SomeBytes(BytesT)
}
type OwnedVariant = Variant<Vec<u8>>;
type BorrowedVariant<'a> = Variant<&'a [u8]>;
Qui, si finisce con due tipi con la relazione "proprietario-di riferimento", che è simile a coppie Vec<T>
- &[T]
, String
- &str
. Documenti suggeriscono tratti built Borrow
e ToOwned
che forniscono solo ciò che è necessario ad eccezione di una sfumatura sottile:
trait Borrow<Borrowed: ?Sized> {
fn borrow(&self) -> &Borrowed;
// this: -----------^
}
pub trait ToOwned {
type Owned: Borrow<Self>;
fn to_owned(&self) -> Self::Owned;
}
Risultato di borrow
è tenuto a essere un riferimento a qualcosa , che BorrowedVariant<'a>
ovviamente non è. La rimozione di questo requisito ovvia a questo problema (qui, i nomi sono preceduti alt sottolineare è un'interfaccia alternativa):
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;
}
Questa caratteristica potrebbe poi essere implementata per tipi standard, ad esempio 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()
}
}
Così come per la Variant
enum in questione:
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()),
}
}
}
Infine, le domande:
- Sto abusando l'originale
Borrow
/ToOwned
concetto? Dovrei usare qualcos'altro per raggiungere questo obiettivo? - In caso negativo, quali sono le ragioni per cui l'attuale interfaccia meno generica di
std::borrow
potrebbe essere stata preferita?