Ограничения типажей высшего порядка (ОТВП, Higher-Rank Trait Bounds (HRTBs))

Типажи Fn в Rust - это уличная магия. Например, мы можем написать следующий код:

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! К тому же, это не какое-то фиксированное время; call работает с любым временем жизни, которое будет у &self в этот момент.

Такая работа требует магии ограничения типажей высшего порядка (ОТВП). Убрать синтаксический сахар можно так:

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)

for<'a> можно прочитать как "для всех возможных 'a", и в общем случае это создаст бесконечный список границ типажа, которым должен соответствовать F. Сильно. Помимо типажей Fn есть не так уж много мест, где мы можем встретить ОТВП. И даже в этих случаях чаще всего нам поможет синтаксический сахар.

В итоге, мы можем переписать оригинальный код более явно:

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());
}