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 的数据的原因也就显而易见了