Why does a borrowed type require a borrowed type to be a reference?

Imagine an event source that creates events represented as an enumeration. Of course, for better performance, this manufacturer has a zero copy, that is, it returns links to its internal buffers:

enum Variant<'a> { Nothing, SomeInt(u64), SomeBytes(&'a [u8]) } impl Producer { fn next(&'a mut self) -> Variant<'a> { ... } } 

This is great for consumers who do not require viewing or returning, but sometimes you need to keep some sequence of events. Thus, our Variant type becomes common:

 enum Variant<BytesT> { Nothing, SomeInt(u64), SomeBytes(BytesT) } type OwnedVariant = Variant<Vec<u8>>; type BorrowedVariant<'a> = Variant<&'a [u8]>; 

Here we get two types with an owner-link relationship, which is similar to Vec<T> - &[T] , String - &str . The docs offer Borrow and ToOwned built-in features that provide only what is required, with the exception of a subtle nuance:

 trait Borrow<Borrowed: ?Sized> { fn borrow(&self) -> &Borrowed; // this: -----------^ } pub trait ToOwned { type Owned: Borrow<Self>; fn to_owned(&self) -> Self::Owned; } 

The result of Borrow should be a reference to something that BorrowedVariant<'a> clearly not. Removing this requirement solves this problem (here the names are prefixed with alt to emphasize that this is an alternative interface):

 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; } 

This trait can be implemented for standard types, for example. 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() } } 

As for the Variant enumeration in question:

 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()), } } } 

Finally, the questions are:

  • Am I using the Borrow / ToOwned concept ToOwned ? Should I use something else for this?
  • If not, what are the reasons why the current less generic interface from std::borrow might be preferable?

This Rust Example

+5
source share
1 answer

Get some explanation at #rust IRC.

From aturon :

short answer: we need higher grade types (HKT) to do better here; it should be possible to smoothly "upgrade" to HKT later, though

(this is a template that appears in several places in the standard library)

(raising the lifetime to the level of traits is a way of encoding HKT, but makes it more inconvenient to use the trait)

From bluss :

I like your question. This type of life in the sign is not sufficiently studied by IMO, but it also has a known error in loan verification right now.

+3
source

All Articles