高阶 Trait 约束 (HRTBs)

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 约束 (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 之外,我们很少遇到 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());
}