假设有一个事件源,产生的事件用枚举表示。为了最大效率,这个生产者是零拷贝的,也就是说它返回指向其内部缓冲区的引用。
enum Variant<'a> {
Nothing,
SomeInt(u64),
SomeBytes(&'a [u8])
}
impl Producer {
fn next(&'a mut self) -> Variant<'a> { ... }
}
以下针对IT技术相关内容,希望对您有所帮助。传统的消费者不需要前瞻或回溯,这样很好,但有时需要保存一些事件序列。因此,我们的Variant
类型变成了通用类型:
enum Variant<BytesT> {
Nothing,
SomeInt(u64),
SomeBytes(BytesT)
}
type OwnedVariant = Variant<Vec<u8>>;
type BorrowedVariant<'a> = Variant<&'a [u8]>;
在这里,我们得到了两种“所有者引用”关系的类型,类似于 Vec<T>
-&[T]
、String
-&str
。文档建议使用内置特性 Borrow
和 ToOwned
,它们提供了所需的功能,但有一个微妙的细节:
trait Borrow<Borrowed: ?Sized> {
fn borrow(&self) -> &Borrowed;
// this: -----------^
}
pub trait ToOwned {
type Owned: Borrow<Self>;
fn to_owned(&self) -> Self::Owned;
}
borrow
的结果必须是对某个东西的引用,而BorrowedVariant<'a>
显然不是。删除这个要求可以解决这个问题(这里,名称前缀为alt以强调这是一种替代接口):
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;
}
这个特性可以为标准类型实现,例如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()
}
}
就所讨论的Variant
枚举而言:
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()),
}
}
}
最后,问题如下:
- 我是否误用了原始的
Borrow
/ToOwned
概念?是否应该使用其他方法来实现此目的? - 如果不是,那么为什么
std::borrow
目前较不通用的接口会更受欢迎呢?