首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >是否可以将`Borrow<T>`转换为`AsRef<T>`,反之亦然?

是否可以将`Borrow<T>`转换为`AsRef<T>`,反之亦然?
EN

Stack Overflow用户
提问于 2020-08-04 12:19:25
回答 2查看 285关注 0票数 1

我有一个变量tokens: &[AsRef<str>],我想将它连接到单个字符串中:

代码语言:javascript
复制
// tokens: &[AsRef<str>]
let text = tokens.join("") // Error
let text = tokens.iter().map(|x|x.as_ref()).collect::<Vec<_>>().join("") // Ok, but...

第二种是尴尬和低效的,因为它将项重新分配给新的Vec

根据源代码的说法,如果join的类型是&[Borrow<str>],则它可以应用于tokens

代码语言:javascript
复制
// if tokens: &[Borrow<str>]
let text = tokens.join("") // OK

// so I want to convert &[AsRef<str>] to &[Borrow<str>]
let text = convert_to_borrow(tokens).join("")

我该怎么做?为什么Join是为实现Borrow而不是AsRef的类型实现的

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2020-08-04 12:35:26

它可能稍微慢一些,但您可以将collect迭代器的&strs直接放入String中。

代码语言:javascript
复制
let text: String = tokens.iter().map(|s| s.as_ref()).collect();

这是可能的,因为FromIterator<&'_ str>。此方法通过反复调用String来增长push_str,这可能意味着必须多次重新分配它,但它不会创建中间Vec<&str>。取决于所使用的片和字符串的大小,这可能要慢一些(虽然在某些情况下也可以稍微快一些)。如果差异对您有重大意义,您应该对这两个版本进行基准测试。

没有办法把T: AsRef<str>的一个片段当作T: Borrow<str>的一个片段,因为并不是所有实现AsRef的东西都实现了Borrow,所以在泛型代码中,编译器无法知道要应用什么Borrow实现。

票数 3
EN

Stack Overflow用户

发布于 2020-08-04 15:47:07

Trentcl的回答仅依靠AsRef<str>实现为您的实际问题提供了解决方案。下面更多的是对标题中更一般性问题的回答。

某些特性带有不变量,实现必须执行这些不变量。特别是,如果Borrow<T>的实现也实现了EqHashOrd,那么对于T的这些特性的实现必须是相同的。这一要求是指借来的价值与原始价值“相同”,但只是以另一种方式看待。例如,String: Borrow<str>实现必须返回整个字符串片;返回子片是不正确的。

AsRef没有这个限制。AsRef<T>的实现可以以与T完全不同的方式实现HashEq等特性。如果您需要返回对结构的一部分的引用,那么AsRef可以这样做,而Borrow不能。

所有这一切都意味着您无法从任意的Borrow<T>实现中派生出有效的AsRef<T>实现:AsRef实现可能不会强制执行Borrow所需的不变量。

然而,相反的方法是有效的。给定任意的AsRef<T>,您可以创建Borrow<T>的实现。

代码语言:javascript
复制
use std::borrow::Borrow;
use std::convert::AsRef;
use std::marker::PhantomData;

pub struct BorrowAsRef<'a, T: ?Sized, U: ?Sized>(&'a T, PhantomData<U>);

impl<'a, T, U> AsRef<U> for BorrowAsRef<'a, T, U>
where
    T: Borrow<U> + ?Sized,
    U: ?Sized,
{
    fn as_ref(&self) -> &U {
        self.0.borrow()
    }
}

pub trait ToAsRef<T: ?Sized, U: ?Sized> {
    fn to_as_ref(&self) -> BorrowAsRef<'_, T, U>;
}

impl<T, U> ToAsRef<T, U> for T
where
    T: ?Sized + Borrow<U>,
    U: ?Sized,
{
    fn to_as_ref(&self) -> BorrowAsRef<'_, T, U> {
        BorrowAsRef(self, PhantomData)
    }
}
代码语言:javascript
复制
fn borrowed(v: &impl Borrow<str>) {
    needs_as_ref(&v.to_as_ref())
}

fn needs_as_ref(v: &impl AsRef<str>) {
    println!("as_ref: {:?}", v.as_ref())
}

为什么Join是为实现Borrow而不是AsRef的类型实现的?

对于所有实现Borrow<str>的类型,这都是一个全面的实现,这意味着它也不能为实现AsRef<str>的类型实现。即使启用了不稳定的特性min_specialization,它也无法工作,因为拥有AsRef的实现并不比拥有Borrow的实现更“特定”。所以他们必须选择一个或另一个。

可以说,AsRef是一个更好的选择,因为它涵盖了更多的类型。但不幸的是,我不认为这一点现在可以改变,因为这将是一个巨大的变化。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/63246747

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档