金属Rust:`Borrow`与`AsRef`

最近在调整Writium的结构,自己果然开始嫌弃Iron麻烦了。造轮子的灵魂觉醒了!那么在包裹Hyper的RequestResponse的过程中,遇到了个问题:到底应该用什么取引用好呢?

std中我们能找到两个用于取引用的trait:BorrowAsRef(以及对应的BorrowMutAsMut)。还是挺容易混淆的,毕竟好像它们的意思都是取引用,“借”走一个对象。那么就先来看看文档是怎么说的。

Borrow

Borrow在文档中的定义如下:

A trait for borrowing data.

简单来说就是告诉用户,从这个类型中能借走另一个类型。在下面的文段中又进一步进行了解释:

When writing generic code, it is often desirable to abstract over all ways of borrowing data from a given type. That is the role of the Borrow trait: if T: Borrow<U>, then &U can be borrowed from &T.

即,Borrow<Borrowed>的类型都是Borrowed的一个更大的概念。比如说,String的结构中含有str(意思到了就行),于是String实现了Borrow<str>,这样就知道Stringstr的一种变式了。

在泛型中,要求泛型参数实现Borrow<Borrowed>所表达的意思是:我需要Borrowed的一种引用形式(值本身、引用或是包裹类型)。自己应当直接或间接拥有这个Borrowed类型的实例。 也就是说,&&T是不可以实现Borrow<T>的。

AsRef

AsRef在文档中的定义如下:

A cheap reference-to-reference conversion. Used to convert a value to a reference value within generic code.

也就是说,AsRef是为了快速取自身引用存在的,只要能够被引用为另一类型、或自身类型的类型都可以实现AsRef。可以认为as_ref进行的操作和下面的代码类似:

(&self).into::<&TargetType>()

在泛型中,要求泛型参数实现AsRef<T>所表达的意思是:我需要一个能被转换为类型T引用的类型。不关心这个T类型的引用所指向的实例是不是自己的一部分。

到底该用什么?

其实我也不是很清楚,这两个trait的界线不是很明晰,下面是我个人的一些看法:

如果仔细看std中两个trait在Stringstr的实现情况的话能够发现,Borrow只由String实现了;而AsRefStringstr两者都实现了。也就是说,如果只是简单的引用转换,实现AsRef就好;如果是返回自身一部分的引用,最好把这两个trait都实现了。

在设计容器类型的时候,使用Borrow可以让容器方法接受键、值类型本身及其引用。参考HashMapget方法实现

如果要在方法里保存一个引用在结构体,可以使用AsRef来获取这个引用。

参考

std::convert::AsRef - Rust std::borrow::Borrow - Rust Borrow and AsRef - The Rust Programming Language