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());
}
Last moify: 2022-12-04 15:11:33
Build time:2025-07-18 09:41:42
Powered By asphinx