Xha*_*lie 5 generics rust borrow-checker
Rust 特征的文档core::convert::AsRef:
\n\n\xe2\x80\xa6
\nBorrow有一个针对任何T,可用于接受引用或值。
然后它继续链接到core::borrow::Borrow特征。
事实上,它可以用来编写可以通过值或通过引用接受参数的通用代码 \xe2\x80\x93 它表示任何可以借用为&Tand 的概念,因为T可以借用为&T,这个简单的例子可以工作完美:
fn report_by_either<T: Borrow<i32>>(either: T) {\n let x: i32 = *either.borrow();\n println!("x = {}", x);\n}\n\xe2\x8b\xae\n\nreport_by_either(5); // x = 5\nreport_by_either(&6); // x = 6\nRun Code Online (Sandbox Code Playgroud)\n如果希望具体地在更复杂的场景中使用Borrow<\xe2\x80\xa6>\xe2\x80\x94 该怎么办:在具有通用约束的通用代码中。不是表示任何借用为 的&T概念,而是如何另外表达这样的约束:T,而是如何另外表达实现特征的
最近,当我试图解决Rust \ 的范围并不全部实现的事实时,我想到了一个非常简单的例子Copy这一事实时,我想到了一个非常简单的例子。
考虑这个函数,它接受任何可以提供的东西RangeBounds<i32>:
fn report_by_value<R: RangeBounds<i32> + Debug>(value: R) {\n println!("range-bounds: `{:?}`", value);\n}\nRun Code Online (Sandbox Code Playgroud)\n这会给调用者带来令人不满意的不一致体验:
\n如果它们通过了实现的类型的某些Copy范围(例如RangeTo,RangeToInclusive , \xe2\x80\xa6),它们就会很好。
let range = ..100;\nreport_by_value(range);\nreport_by_value(range);\nreport_by_value(range);\nRun Code Online (Sandbox Code Playgroud)\n但是,对于其他范围(例如Range, RangeFrom, \xe2\x80\xa6),他们最好调用clone(),否则范围的所有权会被盗:
let range_from = 1..;\nreport_by_value(range_from.clone());\nreport_by_value(range_from.clone());\n\nreport_by_value(range_from);\nreport_by_value(range_from); // use of moved value: `range_from`\nRun Code Online (Sandbox Code Playgroud)\n避免这种不一致的一种方法是通过引用接受范围:
\nfn report_by_reference<R: RangeBounds<i32> + Debug>(reference: &R) {\n println!("range-bounds: `{:?}`", reference);\n}\nRun Code Online (Sandbox Code Playgroud)\n但这也导致调用站点的代码变得笨拙:
\nreport_by_reference(&(4..));\nreport_by_reference(&(..5));\nreport_by_reference(&(6..7));\nRun Code Online (Sandbox Code Playgroud)\n看起来显而易见的解决方案是使用borrow::Borrow:
fn report_by_either<R: RangeBounds<i32> + Debug, T: Borrow<R>>(either: T) {\n println!("range-bounds: `{:?}`", either.borrow());\n}\nRun Code Online (Sandbox Code Playgroud)\n但不幸的是,这引发了这个问题,因为泛型类型的类型推断对此不起作用。
\n以下两个调用都会产生错误:“无法推断函数上声明的类型参数的类型Rreport_by_either”:
report_by_either(5..); // cannot infer type of the type parameter `R` declared on the function `report_by_either`\nreport_by_either(&(6..)); // cannot infer type of the type parameter `R` declared on the function `report_by_either`\nRun Code Online (Sandbox Code Playgroud)\n这些涡轮鱼枯萎线确实有效:
\nreport_by_either::<RangeFrom<i32>, _>(7..);\nreport_by_either::<RangeFrom<i32>, _>(&(8..));\nRun Code Online (Sandbox Code Playgroud)\n在这种特殊情况下,我不希望我的用户必须理解甚至意识到范围类型未实现的Copy原因或意味着并非所有范围类型都未实现的不一致。我希望我的用户能够将任何提供给我的东西传递给我RangeBounds<\xe2\x80\xa6>。也就是说,在本例中,通过引用仅接受范围边界并不是我的 API 中的灾难性妥协 \xe2\x80\x93report_by_reference(&(6..7))很笨重但可以忍受。
然而,更一般地说,我认为任何借用为特征的概念肯定是一种普遍的愿望。
\n我应该如何实现它?
\n你在这里很不走运;您有两个间接特征,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);
}
Run Code Online (Sandbox Code Playgroud)
如果允许省略显式类型参数,编译器应该使用哪种实现?Range<i32>?还是我的Gobbledygook类型?为什么?编译器不会采用其中一种方式。
因此,如果您有T: Trait<U>, U: Borrow<V>或T: Borrow<U>, U: Trait<V>总是必须指定中间类型。尽管如果Trait使用关联类型而不是泛型类型参数,则可以在前一种情况下明确推导中间类型。