高阶 Trait 边界 (HRTB)
Rust 的 Fn
trait 有点神奇。例如,我们可以编写以下代码
struct Closure<F> { data: (u8, u16), func: F, } impl<F> Closure<F> where F: Fn(&(u8, u16)) -> &u8, { fn call(&self) -> &u8 { (self.func)(&self.data) } } fn do_it(data: &(u8, u16)) -> &u8 { &data.0 } fn main() { let clo = Closure { data: (0, 1), func: do_it }; println!("{}", clo.call()); }
如果我们尝试像在生命周期部分那样简单地对这段代码进行脱糖,我们会遇到一些麻烦
// NOTE: `&'b data.0` and `'x: {` is not valid syntax!
struct Closure<F> {
data: (u8, u16),
func: F,
}
impl<F> Closure<F>
// where F: Fn(&'??? (u8, u16)) -> &'??? u8,
{
fn call<'a>(&'a self) -> &'a u8 {
(self.func)(&self.data)
}
}
fn do_it<'b>(data: &'b (u8, u16)) -> &'b u8 { &'b data.0 }
fn main() {
'x: {
let clo = Closure { data: (0, 1), func: do_it };
println!("{}", clo.call());
}
}
我们到底应该如何在 F
的 trait 边界上表达生命周期?我们需要在那里提供一些生命周期,但是我们关心的生命周期在我们进入 call
的主体之前无法命名!此外,这并不是某个固定的生命周期;call
可以处理 &self
在那时碰巧拥有的任何生命周期。
这项工作需要高阶 Trait 边界 (HRTB) 的魔力。我们对它进行脱糖的方式如下
where for<'a> F: Fn(&'a (u8, u16)) -> &'a u8,
或者
where F: for<'a> Fn(&'a (u8, u16)) -> &'a u8,
(其中 Fn(a, b, c) -> d
本身只是对不稳定的真实 Fn
trait 的语法糖)
for<'a>
可以理解为“对于 'a
的所有选择”,并且基本上会生成 F 必须满足的 trait 边界的无限列表。厉害吧。除了 Fn
trait 之外,我们很少遇到 HRTB,即使对于那些情况,我们也为常见情况提供了很好的语法糖。
总而言之,我们可以更明确地重写原始代码,如下所示
struct Closure<F> { data: (u8, u16), func: F, } impl<F> Closure<F> where for<'a> F: Fn(&'a (u8, u16)) -> &'a u8, { fn call(&self) -> &u8 { (self.func)(&self.data) } } fn do_it(data: &(u8, u16)) -> &u8 { &data.0 } fn main() { let clo = Closure { data: (0, 1), func: do_it }; println!("{}", clo.call()); }