Rust的core::convert::AsRef
特质文档中表示:
…
Borrow
针对于任意类型T
都有一个默认实现,因此可以用来接受引用或值。
随后它继续介绍了core::borrow::Borrow
特质。
这确实可以用于编写通用代码,以便可以通过值或引用接受参数,它代表了可以作为&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的范围不都实现了Copy
的事实时。
考虑这个函数,它接受任何可以提供RangeBounds<i32>
的东西:
fn report_by_value<R: RangeBounds<i32> + Debug>(value: R) {
println!("range-bounds: `{:?}`", value);
}
这会导致呼叫者体验不一致的问题:
If they pass some ranges, of the types that implement
Copy
(e.g.RangeTo
,RangeToInclusive
, …), they'll be just fine.let range = ..100; report_by_value(range); report_by_value(range); report_by_value(range);
But, for other ranges (e.g.
Range
,RangeFrom
, …) they'd better callclone()
or ownership of the range gets stolen: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());
}
不幸的是,泛型类型的类型推断对此无效。
Both of the following calls yield an error: "cannot infer type of the type parameter
R
declared on the functionreport_by_either
":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`
These turbo-fish-blighted lines do work:
report_by_either::<RangeFrom<i32>, _>(7..); report_by_either::<RangeFrom<i32>, _>(&(8..));
Copy
或者存在不一致性导致并非所有范围类型都没有实现它们的原因。我希望我的用户能够向我传递任何给出RangeBounds<...>
的东西。话虽如此,在这种情况下,仅接受范围边界的引用并不是我的API中的灾难性妥协-report_by_reference(&(6..7))
很笨拙但还可以容忍。然而,更普遍地说,我认为“任何借来的东西作为特质”的概念肯定是常见的需求。
我该如何实现呢?
AsRef
的通用实现。事实上,core::convert::AsRef
的文档特别指向了Borrow
,就是为了这个原因。 - Xharlie