为什么Borrow和AsRef的实现方式不同?

17
BorrowAsRef特质在Rust中非常相似。如果我理解正确,它们在被实现于同一类型时有着完全相同的签名(除了方法名),区别在于它们的使用方式。
但当你查看它们的实现,比如在Vec<T>上,会有一个微妙的差别: slice.rs:
#[stable(feature = "rust1", since = "1.0.0")]
impl<T> Borrow<[T]> for Vec<T> {
    fn borrow(&self) -> &[T] {
        &self[..]
    }
}

vec.rs:

#[stable(feature = "rust1", since = "1.0.0")]
impl<T> AsRef<[T]> for Vec<T> {
    fn as_ref(&self) -> &[T] {
        self
    }
}

我认为这里实现的AsRef之所以有效是因为Deref强制转换。但是同样的机制是否可以用于Borrow实现呢?


1
我一直在摆弄这个问题 - 仍然不确定为什么这里的实现不同,但我可以确认您关于Deref coercion支持AsRef实现的理论。 - Reid Rankin
2
我不确定你的问题是否能够得到答案。两个执行同一任务的函数写法略有不同,这并不一定有任何原因。毕竟,通常来说,编写代码的方式也没有唯一的标准方法。 - jthulhu
1
它们是由不同的人在不同的时间编写的(而且Borrow更早)。这不够了吗? - Chayim Friedman
1
我认为 AsRef 的实现是在说:“给出底层切片的引用”。与此同时,Borrow 的实现则是在说:“从向量中借用子切片 ..(即所有元素)”。话虽如此,这两者之间的区别可能是语义上的,也可能是为了让编译器知道在 Borrow 中你正在借用一个子切片而不是整个向量本身。 - Terens Tare
1
对于未来的读者,如果想了解AsRefBorrow特质之间的实际区别,这里的答案提供了一些有用的见解:为什么T没有实现AsRef<T>? - kmdreko
2个回答

6

这两种实现没有实际区别。

您对语义差异的评估是正确的:&self[..]主体使用索引运算符与开放范围创建整个向量的切片,而基本的self主体则依赖于Deref coercion,其中编译器自动将&Vec<T>转换为&[T]。

没有功能上的区别。正如您所指出的,方法签名是相同的,因此在实现之外对借用检查器也是相同的。上面的评论建议在从向量中借用和从该向量借用切片之间存在内部差异,但这并不是事实;在两种情况下,切片的生命周期均源自self。两种方法生成的汇编代码也是相同的;尽管它们使用不同的方式来获得相同的结果,优化有时会混乱,但如果在任何情况下存在任何代码生成差异,我会感到惊讶。

所以唯一的区别在于风格。这两种实现早在Rust 1.0发布之前就已经存在了。正如上面的评论所提到的,Borrow实现更早,而Deref特质在其编写时确实存在,但只是为Vec新鲜实现的(一个月前),直到几个月后才最终确定。因此,原始的Borrow实现使用了预解引用语法并不奇怪。而AsRef实现直到Deref被确定后才存在,因此作者使用它也不奇怪。

因此,您看到的差异只是无关紧要的历史问题。


1
一个网站给出了一个非常有用的答案:
我们可以看到它们有点相似:它们都处理某种类型的拥有和借用版本。然而,它们有一些不同之处。
当您想抽象出不同类型的借用时,或者当您正在构建将拥有值和借用值等效处理的数据结构(例如哈希和比较)时,请选择“Borrow”。
当您想直接将某些内容转换为引用,并编写通用代码时,请选择“AsRef”。
——来源:Source

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