"impl Trait"返回类型导致错误的生存期省略



返回类型的impl Trait语法似乎会导致编译器错误地认为,在某些情况下,输入参数的生存期必须与输出匹配。考虑功能

fn take_by_trait<T: InTrait>(_: T) -> impl OutTrait {}

如果输入类型包含生存期,则编译器会在输出超过生存期时发出抱怨,即使它们是完全独立的。如果输入类型不是泛型,或者输出是Box<dyn OutTrait>,则不会发生这种情况。

完整代码:

trait InTrait {}
struct InStruct<'a> {
_x: &'a bool, // Just a field with some reference
}
impl<'a> InTrait for InStruct<'a> {}
trait OutTrait {}
impl OutTrait for () {}
fn take_by_type(_: InStruct) -> impl OutTrait {}
fn take_by_trait<T: InTrait>(_: T) -> impl OutTrait {}
fn take_by_trait_output_dyn<T: InTrait>(_: T) -> Box<dyn OutTrait> {
Box::new(())
}
fn main() {
let _ = {
let x = true;
take_by_trait(InStruct{ _x: &x }) // DOES NOT WORK
// take_by_type(InStruct { _x: &x }) // WORKS
// take_by_trait_output_dyn(InStruct { _x: &x }) // WORKS
};
}

这里是否有一些被忽略的生命周期,我有资格让它发挥作用,或者我需要进行堆分配?

impl Trait语义意味着函数返回实现Trait的某个类型,但调用方不能对该类型或使用寿命做出任何假设。

众所周知,take_by_trait函数可以在许多不同的模块中使用,或者可能在其他板条箱中使用。现在,您的实现在所有用例中都可以正常工作。它可以像一样重写

fn take_by_trait<T: InTrait>(_: T) -> () {}

这是一个非常好的功能,工作起来也很好。但是,在某个时刻,您可能需要为OutTrait添加另一个实现,并稍微更改take_by_trait函数。

trait OutTrait { fn use_me(&self) {} }
impl<T: InTrait> OutTrait for T {}
fn take_by_trait<T: InTrait>(v: T) -> impl OutTrait {v}

如果我们扩展通用参数和impl的定义,我们得到的代码是:

fn take_by_trait<'a>(v: InStruct<'a>) -> InStruct<'a> {v}
fn main() {
let v = {
let x = true;
take_by_trait(InStruct{ _x: &x })
};

v.use_me();
}

这显然不起作用,因为xprintln!尝试访问其值之前就被丢弃了。因此,通过为OutTrait添加一个新的实现,您破坏了使用该函数的代码,可能是在机箱中的某个位置,这取决于您的。这就是为什么编译器不愿意允许您定义这样的东西。

因此,impl OutTrait的问题再次出现,只是编译器不能对返回的类型及其生存期做出任何假设,所以它使用了最大可能的边界,这会产生您看到的借用检查器错误。

编辑:我对代码做了一些修改,这样函数的签名就不会改变,而且代码实际上编译并产生了相同的终身错误:操场

impl Trait在返回位置隐式捕获泛型参数中出现的任何生存期。这不是在普通Rust中可以表达的,只能用impl Trait来表达。

据我所知,在马厩里没有办法避免这种情况。在夜间,您可以使用type_alias_impl_trait:

#![feature(type_alias_impl_trait)]
type Ret = impl OutTrait;
fn take_by_trait<T: InTrait>(v: T) -> Ret {}

请参阅问题当impl Trait返回值捕获非静态参数(#82171(,impl Trait捕获类型参数的生存期(#79415(,假阳性时的编译器错误;借用时临时丢弃";涉及返回位置impl Trait(#98997(,如果从泛型函数(#76882(返回,则impl Trait+的static不是静态的。

相关内容

最新更新