特性说:
…
Borrow对任何T都有一个覆盖式的强制执行,并且可以用于接受引用或值。
然后,它继续到特性。
实际上,这可以用来编写泛型代码,这些代码可以通过值或引用接受参数--它代表了任何可以借用为&T的概念,而且由于T可以借用为&T,所以这个简单的示例工作得很好:
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的东西
fn report_by_value<R: RangeBounds<i32> + Debug>(value: R) {
println!("range-bounds: `{:?}`", value);
}这会给来电者带来一种令人不满意的不一致体验:
Copy的类型(例如RangeTo、RangeToInclusive、…)他们会没事的。
设range = ..100;report_by_value(范围);report_by_value(范围);report_by_value(范围);Range、RangeFrom、…)他们最好打电话给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- [ _playground_](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=dcd0a541d8b7e51bc09c43d6fda89fa8)避免这种不一致的一种方法就是通过引用来接受范围:
fn report_by_reference<R: RangeBounds<i32> + Debug>(reference: &R) {
println!("range-bounds: `{:?}`", reference);
}但这也会导致呼叫站点出现笨拙的代码:
report_by_reference(&(4..));
report_by_reference(&(..5));
report_by_reference(&(6..7));似乎最明显的解决方案是使用borrow::Borrow
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的类型在这个特别的场景中,我不希望我的用户必须理解甚至知道Copy或不一致意味着并不是所有的的原因。我希望我的用户能够传递给我任何给RangeBounds<…>的东西。尽管如此,在我的API中,只接受引用范围范围并不是一种荒谬的妥协-- report_by_reference(&(6..7))是笨重的,但可以容忍。
然而,更广泛地说,我认为任何借来的特质的概念肯定是一种共同的需要。
我应该如何实现这一目标?
发布于 2022-10-14 00:17:55
您在这里非常不走运;在6..7和&i32之间有两个间接特性,编译器无法推断中间类型。例如,我可以创建一个可以满足中间类型的约束的类型:
#[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使用关联类型而不是泛型类型参数,那么在前一种情况下可以明确推断中间类型。
https://stackoverflow.com/questions/74056427
复制相似问题