2015-05-06 15 views
5

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:

  1. jestem nadużywania oryginalny Borrow/ToOwned koncepcję? Czy powinienem użyć czegoś innego, aby to osiągnąć?
  2. Jeśli nie, to jakie są powody, dla których preferowany był obecny mniej ogólny interfejs z std::borrow?

This example on Rust playpen

Odpowiedz

3

Got some explanation na #rust IRC.

Od aturon:

krótka odpowiedź brzmi: my potrzebujemy wyższego kinded typów (HKT) zrobić lepiej tutaj; powinno być możliwe, aby płynnie „upgrade” do HKT później, chociaż

(jest to wzór, który jest wymyślić kilka miejsc w bibliotece standardowej)

(podnosząc żywotność na poziomie cecha jest sposobem kodowania HKT, ale sprawia, że ​​znacznie bardziej niewygodne w użyciu cechę)

od bluss:

Lubię swoje pytanie. Ten rodzaj życia w cechach nie został wystarczająco zbadany IMO , ale ma również znany błąd w kontrolerze pożyczek już teraz