更高级别的生命周期和泛型不友好

3
在这里的代码中:
trait Foo {
    type Output;
    fn foo(self) -> Self::Output;
}

impl<'a> Foo for &'a () {
    type Output = &'a ();
    fn foo(self) -> Self::Output {
        self
    }
}

fn func<F: Foo>(f: F) -> F::Output {
    f.foo()
}

fn func2<'a>(f: &'a ()) -> &'a () {
    func::<&'a ()>(f)
}

fn has_hrl<F: Fn(&()) -> &()>(f: F) {}

fn main() {
    //has_hrl(func); // FAILS
    has_hrl(func2);
    has_hrl(|x| func(x));
}

我们希望使用has_hrl(func),但是Rust只接受闭包has_hrl(|x| func(x))。为什么会这样呢?因为它适用于像func2中的具体类型,但不适用于通用类型。

如果你像这样声明 has_hrl,就可以正常工作:fn has_hrl<'a, F: Fn(&'a ()) -> &'a ()>(_: F) {}。虽然我无法解释为什么生命周期需要显式声明。 - ljedrz
1个回答

8
在这个表达式中:
has_hrl(func)

编译器被迫选择一个特定的func实例。对于所有的'a&'a ()实现了Foo,但是编译器只能选择一个特定的'a来实例化func,因为类型变量不能表示多种类型。因此,func::<&'a ()>没有实现for<'a> Fn(&'a ()) -> &'a (),它只为一个特定的生命周期'x实现了Fn(&'x ()) -> &'x ()

如果您像这样声明has_hrlfn has_hrl<'a, F: Fn(&'a ()) -> &'a ()>(_: F) {},那么它将起作用。我的能力不足以解释为什么生命周期需要明确指出。- ljedrz

那是因为原始声明具有隐含的更高级别的生命周期限制(该限制等同于F: for<'a> Fn(&'a ()) -> &'a ()),这意味着F必须为所有 生命周期 'a 实现Fn(&'a ()) -> &'a ()。您的版本仅要求F为一个具体生命周期实现Fn(&'a ()) -> &'a ()。您还会发现,如果has_hrl尝试使用仅在has_hrl函数本地的生命周期调用闭包,则此版本无法正常工作,因为调用方不可能将该生命周期作为参数传递(这就是为什么引入了更高级别生命周期限制的原因)。

我刚试着将语句has_hrl(|x| func(x));拆分成两个语句:let f = |x| func(x); has_hrl(f);,但是同样的错误出现了。某种程度上,参数的处理方式与变量绑定不同(绑定应该固定为完全具体的类型)。因此,我猜想即使是更高级别的生命周期在 Rust 当前的类型系统中也没有被很好地捕获(并且在幕后使用了一些技巧)? - John
我知道它有更高的生命周期,但我仍然不明白为什么它不起作用,这似乎是应该可以工作的事情。 - iopq
@iopq: 你并不孤单... - Francis Gagné
1
你的第一部分并不完全正确。编译器可以为所有 'a 实例化单个 func::<&'a ()>,它在 func2 中正是这样做的。也就是说,rustc 实例化了一个 func2 的副本,这意味着必须存在一个对于所有 'a 都有效的单个 func::<&'a ()>。不幸的是,rustc 在类型系统中似乎没有暴露这个事实。 - Steven
@Steven:确实存在一个单一的 func::<&'a ()> 适用于所有 'a,因为生命周期参数是擦除的,而类型参数不是。这可能是为什么我们有高阶生命周期但没有高阶类型(例如 for<T> Fn(T))的原因,后者可能更难实现。 - Francis Gagné

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接