引用参数化函数的结构上的终身错误

时间:2018-11-20 05:25:29

标签: rust

如果我在下面编写代码,则会得到error[E0309]: the parameter type 'T' may not live long enough

struct Function<T> {
    f: fn() -> T,
}

struct FunctionRef<'f, T> {
    f: &'f Function<T>,
}

这可以修复错误:

struct FunctionRef<'f, T: 'f> {
    f: &'f Function<T>,
}

但是,据我所知,T不受生存期'f的限制。实际上,T是运行类型fn () -> T的函数时创建的新对象。

我在哪里缺少什么?

1 个答案:

答案 0 :(得分:4)

简短的回答:您需要T: 'f,因为T可能包含保存引用的字段,并且fn() -> TT上是协变的。

简化事情可能有助于理解...

暂时将fn() -> T替换为T,因为对我来说,更容易解释生命周期中发生的事情。 请参阅下面的注释,为什么使用这种替换方法不会更改与寿命相关的错误。

struct Function<T> {
    f: T,
}

struct FunctionRef<'f, T> {
    f: &'f Function<T>,
}

这会导致相同的error[E0309]: the parameter type 'T' may not live long enough错误。

FunctionRef实例不能超过其在f字段中拥有的引用: 您在尖括号内声明一个通用生命周期参数'f,然后使用'f作为注释 在结构体内。另请参见book

但是FunctionRef::f取决于类型参数T。显式约束T: 'f保证有T个实例 不会超过T拥有的引用,并且FunctionRef不会超过FunctionRef::f

如果可以帮助您理解,将通用T替换为特定的Foo类型:

struct Foo<'a> {
    n: &'a i32,
}

struct FunctionRef<'f, 'a: 'f> {
    f: &'f Foo<'a>,
}

您需要限制生存期'a的有效期至少与'f生存期一样长,否则会违反内存安全规则。

注意

我考虑了f: T等同于f: fn() -> T的情况,因为这种类型的构造函数在T上是协变的。

要了解fn() -> TT的协变关系,请考虑以下结构:

struct Foo<'a> {
    v: &'a i32
}

在这种情况下,可以安全地将v的生命周期值赋予'a,例如:

let ref_value: &'static i32 =  &100;
let foo = Foo { v: ref_value};

现在,以下结构也是如此:

struct Function<'a> {
    f: fn() -> &'a i32
}

字段f需要一个返回&i32的{​​{1}}的函数。

在这种情况下,可以安全地返回一个返回“ 'a”且寿命更长的函数,例如:

&i32

背后有很多类型理论,see the nomicom进行了详细说明。