首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何使用`borrow::Borrow<…接受任何“借来的”特质?

如何使用`borrow::Borrow<…接受任何“借来的”特质?
EN

Stack Overflow用户
提问于 2022-10-13 13:16:49
回答 1查看 65关注 0票数 4

特性说:

Borrow对任何T都有一个覆盖式的强制执行,并且可以用于接受引用或值。

然后,它继续到特性

实际上,这可以用来编写泛型代码,这些代码可以通过值或引用接受参数--它代表了任何可以借用为&T的概念,而且由于T可以借用为&T,所以这个简单的示例工作得很好:

代码语言:javascript
复制
fn report_by_either<T: Borrow<i32>>(either: T) {
    let x: i32 = *either.borrow();
    println!("x = {}", x);
}
⋮

report_by_either(5); // x = 5
report_by_either(&6); // x = 6

如果您希望在更复杂的场景中使用Borrow<…> --具体而言:在具有泛型约束的泛型代码中。与其将借用的任何东西的概念表示为&T,还如何表示T实现一个特性的约束呢?

最近,当我试图绕过Copy这个事实时,我想到了一个非常简单的例子。

考虑一下这个函数,它接受任何可以提供RangeBounds的东西

代码语言:javascript
复制
fn report_by_value<R: RangeBounds<i32> + Debug>(value: R) {
    println!("range-bounds: `{:?}`", value);
}

这会给来电者带来一种令人不满意的不一致体验:

  1. 如果它们通过某些范围,实现Copy的类型(例如RangeToRangeToInclusive、…)他们会没事的。 设range = ..100;report_by_value(范围);report_by_value(范围);report_by_value(范围);
  2. 但是,对于其他范围(例如RangeRangeFrom、…)他们最好打电话给clone(),否则范围的所有权就会被偷: 设range_from =1.;report_by_value(range_from.clone());report_by_value(range_from.clone());report_by_value(range_from);report_by_value(range_from);//移动值的使用:range\_from
代码语言:javascript
复制
- [ _playground_](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=dcd0a541d8b7e51bc09c43d6fda89fa8)

避免这种不一致的一种方法就是通过引用来接受范围:

代码语言:javascript
复制
fn report_by_reference<R: RangeBounds<i32> + Debug>(reference: &R) {
    println!("range-bounds: `{:?}`", reference);
}

但这也会导致呼叫站点出现笨拙的代码:

代码语言:javascript
复制
report_by_reference(&(4..));
report_by_reference(&(..5));
report_by_reference(&(6..7));

似乎最明显的解决方案是使用borrow::Borrow

代码语言:javascript
复制
fn report_by_either<R: RangeBounds<i32> + Debug, T: Borrow<R>>(either: T) {
    println!("range-bounds: `{:?}`", either.borrow());
}

然而不幸的是,这引发了这个问题,因为泛型类型的类型推断对此不起作用。

  • 以下两个调用都会产生一个错误:“无法推断函数R上声明的类型参数report_by_either的类型”: Report_by_either(5.);//无法推断在函数report\_by\_either report_by_either(&(6.))上声明的类型参数R的类型;//无法推断函数report\_by\_either上声明的类型参数R的类型
  • 这些涡轮鱼线是有用的: report_by_either::(7.);report_by_either::(&(8.));

在这个特别的场景中,我不希望我的用户必须理解甚至知道Copy不一致意味着并不是所有的的原因。我希望我的用户能够传递给我任何给RangeBounds<…>的东西。尽管如此,在我的API中,只接受引用范围范围并不是一种荒谬的妥协-- report_by_reference(&(6..7))是笨重的,但可以容忍。

然而,更广泛地说,我认为任何借来的特质的概念肯定是一种共同的需要。

我应该如何实现这一目标?

EN

回答 1

Stack Overflow用户

发布于 2022-10-14 00:17:55

您在这里非常不走运;在6..7&i32之间有两个间接特性,编译器无法推断中间类型。例如,我可以创建一个可以满足中间类型的约束的类型:

代码语言:javascript
复制
#[derive(Debug)]
struct Gobbledygook;

impl RangeBounds<i32> for Gobbledygook {
    fn start_bound(&self) -> Bound<&i32> { todo!() }
    fn end_bound(&self) -> Bound<&i32> { todo!() }
}

impl Borrow<Gobbledygook> for Range<i32> {
    fn borrow(&self) -> &Gobbledygook { todo!() }
}

fn report_by_either<R: RangeBounds<i32> + Debug, T: Borrow<R>>(either: T) {
    println!("range-bounds: `{:?}`", either.borrow());
}

fn main() {
    report_by_either::<Gobbledygook, _>(0..7);
}

如果允许省略显式类型参数,编译器应该使用哪个实现?Range<i32>?还是我的Gobbledygook型?为什么?编译器不会以这样或那样的方式假设。

因此,如果您有T: Trait<U>, U: Borrow<V>T: Borrow<U>, U: Trait<V>,则始终必须指定中间类型。虽然如果Trait使用关联类型而不是泛型类型参数,那么在前一种情况下可以明确推断中间类型。

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

https://stackoverflow.com/questions/74056427

复制
相关文章

相似问题

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