documentation for Rust's core::convert::AsRef
trait表示:
... Borrow
对任何T
都有一个blanket impl,并且可以用来接受引用或值。
然后继续执行link to the core::borrow::Borrow
trait。
实际上,它可以用来编写泛型代码,通过传值或引用来接受参数--它代表了 * 的概念,而不是可以作为&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
* 的东西的概念?
最近,当我试图解决Rust's ranges do not all implement Copy
的问题时,我想到了一个非常简单的例子。
考虑这个函数,它接受任何可以提供RangeBounds<i32>
的函数:
fn report_by_value<R: RangeBounds<i32> + Debug>(value: R) {
println!("range-bounds: `{:?}`", value);
}
这导致呼叫者的不满意的不一致体验:
1.如果它们传递了实现Copy
的类型(例如RangeTo
、RangeToInclusive
...)的 * 一些 * 范围,它们就不会有问题。
let range = ..100;
report_by_value(range);
report_by_value(range);
report_by_value(range);
let 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); // use of moved value: `range_from`
避免这种不一致性的一种方法是通过引用接受范围:
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());
}
然而,不幸的是,这引发了这个问题,因为泛型类型的类型推理对此不起作用。
- 以下两个调用都会产生错误:“* 无法推断在函数
report_by_either
* 上声明的类型参数R
的类型":
report_by_either(5..); // cannot infer type of the type parameter `R` declared on the function `report_by_either`
report_by_either(&(6..)); // cannot infer type of the type parameter `R` declared on the function `report_by_either`
- 这些 turbo-fish-枯萎线工作:
report_by_either::<RangeFrom<i32>, _>(7..);
report_by_either::<RangeFrom<i32>, _>(&(8..));
在这个特定的场景中,我不希望我的用户必须理解甚至意识到why the range-types don't implement Copy
或inconsistencies that mean that not all of them don't的原因。我希望我的用户能够传递给我任何给出RangeBounds<…>
的东西。也就是说,在这个例子中,在我的API中只接受范围边界并不是一个灾难性的妥协-report_by_reference(&(6..7))
很笨拙,但可以忍受。
然而,更一般地说,我认为“任何作为一种特质而借用的东西”的概念肯定是一种普遍的需要。
我应该如何实现它?
1条答案
按热度按时间hgqdbh6s1#
你在这里运气不好在
6..7
和你的&i32
之间有两个间接特性,编译器不能推导出中间类型。例如,我可以创建一个类型,它满足 could 是中间类型的约束:如果允许省略显式类型参数,编译器应该使用哪种实现?
Range<i32>
?还是我的Gobbledygook
类型?为什么?编译器不会采取这种或那种方式。所以如果你有
T: Trait<U>, U: Borrow<V>
或T: Borrow<U>, U: Trait<V>
,你总是需要指定中间类型。尽管如果Trait
使用了一个关联类型而不是泛型类型参数,那么在前一种情况下,中间类型可以被明确地推导出来。