Stellen Sie sich eine Ereignisquelle vor, die Ereignisse als Enum darstellt. Natürlich, für beste Effizienz dieser Hersteller Null-Kopie, also Verweise auf seine internen Puffer zurückgibt:Warum benötigt das Borrow-Merkmal den geborgten Typ als Referenz?
enum Variant<'a> {
Nothing,
SomeInt(u64),
SomeBytes(&'a [u8])
}
impl Producer {
fn next(&'a mut self) -> Variant<'a> { ... }
}
Das ist völlig in Ordnung, für die Verbraucher, die Look-Ahead oder Rückzieher nicht benötigen, aber manchmal ist es ein müssen einige Sequenz von Ereignissen speichern. So wird unser Variant
Typ ein generisches:
enum Variant<BytesT> {
Nothing,
SomeInt(u64),
SomeBytes(BytesT)
}
type OwnedVariant = Variant<Vec<u8>>;
type BorrowedVariant<'a> = Variant<&'a [u8]>;
Hier haben wir mit zwei Typen mit „owner-reference“ Beziehung am Ende, die Paare Vec<T>
ähnlich ist - &[T]
, String
-&str
. Docs vorschlagen gebautet Züge Borrow
und ToOwned
, die gerade das, was bis auf eine subtile Nuance erforderlich:
trait Borrow<Borrowed: ?Sized> {
fn borrow(&self) -> &Borrowed;
// this: -----------^
}
pub trait ToOwned {
type Owned: Borrow<Self>;
fn to_owned(&self) -> Self::Owned;
}
Ergebnis borrow
benötigt ein Bezug auf etwas, zu sein, die BorrowedVariant<'a>
ist offensichtlich nicht. Das Entfernen dieser Anforderung löst dieses Problem (hier Namen mit alt Präfix die Tatsache zu betonen, dies ist eine alternative Schnittstelle):
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;
}
Diese Eigenschaft dann für Standardtypen umgesetzt werden könnten, beispielsweise 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()
}
}
als auch für die Variant
Enum in Frage:
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()),
}
}
}
, schließlich die Fragen:
- Bin ich das Original
Borrow
/ToOwned
Konzept zu mißbrauchen? Soll ich etwas anderes dafür verwenden? - Wenn nicht, was sind dann die Gründe, warum die aktuelle weniger generische Schnittstelle von
std::borrow
hätte bevorzugt werden können?