iovxw

Rust 中的高阶生命期约束

Ruskell

这篇文章并不在讲什么是生命期, 请先确保有正确的生命期概念

虽然标题是说高阶生命期约束, 不过 RFC 本身是叫高阶 Trait 约束 (Higher Ranked Trait Bounds), 但由于目前只实现了生命期的部分, 所以……

言归正传, 在 serde 里, 会发现 DeserializeOwned 的定义是这样的

pub trait DeserializeOwned: for<'de> Deserialize<'de> { }

这算什么语法? 教程里没写啊!

其实这是一个在闭包里经常被用到的特性, 只是不需要显式去写

为了说明, 首先让我们实现一个闭包:

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

乍一看什么毛病也没有, 很符合逻辑, 外部环境被包装到 data 里传递到函数形成闭包

但这上面引用类型生命期都是隐式的, 我们都知道 Rust 会在编译时给它去糖展开再编译, 像这样:

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

问题来了, 由于 'a 是在 call 时才定义的, 我们无法写出 F 的约束

要是懂 Haskell 的, 肯定说有个东西叫 Rank N Type 可以解决这个问题, 而 Rust 实现了它的一部分, 只针对生命期

语法为 for<'a> Foo<'a>

代表 'a 的所有可能

通俗点来说就是把这个泛型放到哪, 他就会满足哪里的约束, 一个例子可以很好的解释:

fn foo<'a, 'b, F>(a: &'a i32, b: &'b i32, f: F) -> (&'a i32, &'b i32)
where
    F: for<'x> Fn(&'x i32) -> &'x i32,
{
    (f(a), f(b))
}

'x 在作用到第一个元素时推导为 'a, 后为 'b

所以上面 Closure 例子里 F 的约束应该这么写:

where for<'a> F: Fn(&'a (u8, u16)) -> &'a u8,

DeserializeOwned 之所以能表示 Owned 的数据的原因也就显而易见了