3.7 高阶 trait 约束(HRTB)

高阶 trait 约束 for<‘a>

译文 · 基于 The Rustonomicon

高阶 trait 约束(HRTB)

原文链接: https://doc.rust-lang.org/nomicon/hrtb.html

  Rust 的 Fn trait 有点「魔法」。例如,我们可以写出如下代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
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 的 trait 约束上的生命周期?那里必须提供某个生命周期,但我们关心的生命周期在进入 call 函数体之前无法命名!而且,那也不是某个固定的生命周期;call 适用于 &self 在调用点任意具有的生命周期。

  这项工作需要高阶 trait 约束(Higher-Rank Trait Bounds,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;即便遇到,常见情形也有便捷的语法糖。

  总之,我们可以把原始代码更明确地改写为:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
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());
}
最后修改 August 11, 2026: 更新 (70a5af133)