2015-05-06 3 views
5

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:

  1. Bin ich das Original Borrow/ToOwned Konzept zu mißbrauchen? Soll ich etwas anderes dafür verwenden?
  2. Wenn nicht, was sind dann die Gründe, warum die aktuelle weniger generische Schnittstelle von std::borrow hätte bevorzugt werden können?

This example on Rust playpen

Antwort

3

Got some explanation auf #rust IRC.

Von aturon:

die kurze Antwort ist: Wir höherer kinded Typen brauchen würde (HKT) hier besser zu machen; es sollte später auf „Upgrade“ HKT reibungslos möglich sein, obwohl

(das ein Muster, das ein paar Plätze in der Standard-Bibliothek nach oben gekommen ist) mit dem Merkmale Ebene

(Anheben die Lebensdauer ist ein Weg, von HKT kodiert, sondern macht es wesentlich umständlicher, das Merkmal zu verwenden)

von bluss:

ich Ihre Frage gerne. Diese Art von Lebensdauer in einem Merkmal wurde nicht genug erforscht IMO aber es hat auch einen bekannten Fehler in der Borrow-Checker jetzt