为什么Borrow trait要求被借用的类型必须是一个引用?

9
假设有一个事件源,产生的事件用枚举表示。为了最大效率,这个生产者是零拷贝的,也就是说它返回指向其内部缓冲区的引用。
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。文档建议使用内置特性 BorrowToOwned,它们提供了所需的功能,但有一个微妙的细节:

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

最后,问题如下:

  1. 我是否误用了原始的 Borrow/ToOwned 概念?是否应该使用其他方法来实现此目的?
  2. 如果不是,那么为什么 std::borrow 目前较不通用的接口会更受欢迎呢?

在 Rust playpen 上的这个示例

1个回答

3

在 #rust IRC 上得到了 一些解释

aturon 的回答如下:

简短的回答是:我们需要高阶类型(HKT)才能更好地做到这一点;不过后期平滑“升级”到 HKT 应该是可能的。

(这是标准库中出现过的一种模式。)

(将生命周期提升到特质级别是一种编码 HKT 的方式,但会使使用特质变得更加笨拙。)

bluss 的回答如下:

我喜欢你的问题。在我的看法中,这种特质中的生命周期还没有被充分探索,但它目前也存在一个已知的借用检查器错误。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接