2015-05-06 16 views
5

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:

  1. Sto abusando l'originale Borrow/ToOwned concetto? Dovrei usare qualcos'altro per raggiungere questo obiettivo?
  2. In caso negativo, quali sono le ragioni per cui l'attuale interfaccia meno generica di std::borrow potrebbe essere stata preferita?

This example on Rust playpen

risposta

3

Got some explanation su #rust IRC.

Da aturon:

la risposta breve è: avremmo bisogno di tipi di alto-kinded (HKT) di fare meglio qui; dovrebbe essere possibile senza intoppi "upgrade" a HKT più tardi, anche se

(questo è un modello che è venuto qualche posizione nella libreria standard)

(sollevando la vita per il livello di carattere è un modo di codificare HKT, ma rende molto più scomodi da usare il tratto)

da bluss:

mi piace la tua domanda. Questo tipo di vita in un tratto non è stato esplorato abbastanza IMO ma ha anche un bug noto nel correttore del prestito al momento