Rust 的 Fn
特征带些许魔法,例如我们可以写出下面的代码:
译自:https://doc.rust-lang.org/nomicon/hrtb.html[高阶特征约束] |
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());
}
如果我们以我们在生命周期部分相同的方式来去糖的话,我们会碰到一些问题:
// 注意: `&'b data.0` 和 `'x: {` 不是有效的表达式。
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 特征约束的生命周期呢?我们需要在这里提供一些生命周期,但是我们关心的生命周期在我们进入 call 之前是无法命名的!而且,这个生命周期不是固定的,只要我们在某时持有 &self
,call 就能使用。
这项工作需要高阶特征约束(Higher-Rank Trait Bounds, HRTBs)帮助。我们以下面的方式去糖:
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 之外,实际上遇到 HRTBs 的地方并不多,而且甚至这些地方都有一些非常不错的 magic 语法糖。
总之,我们能够更显式地重写原始代码:
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());
}