[英]Adding unrelated generic parameter triggers strange lifetime error
我有一个特性,我想为所有实现std::ops::Index
类型实现它。 这段代码有效(正如我所料):
use std::ops::Index;
use std::fmt::Display;
trait Foo {
fn foo(&self, i: usize) -> &Display;
}
impl<C> Foo for C
where
C: Index<usize>,
C::Output: Display + Sized,
{
fn foo(&self, i: usize) -> &Display {
&self[i]
}
}
( 游乐场 )
但是 ,一旦我将一个通用参数引入我的特性,我就会遇到奇怪的生命周期错误。 这是代码( Playground ):
trait Foo<T> {
fn foo(&self, i: T) -> &Display;
}
impl<C, T> Foo<T> for C
where
C: Index<T>,
C::Output: Display + Sized,
{
fn foo(&self, i: T) -> &Display {
&self[i]
}
}
而奇怪的错误(显然这是一个错误,在略有不同的版本中重复三次):
error[E0311]: the associated type `<C as std::ops::Index<T>>::Output` may not live long enough
--> src/main.rs:15:9
|
15 | &self[i]
| ^^^^^^^^
|
= help: consider adding an explicit lifetime bound for `<C as std::ops::Index<T>>::Output`
note: the associated type `<C as std::ops::Index<T>>::Output` must be valid for the anonymous lifetime #1 defined on the method body at 14:5...
--> src/main.rs:14:5
|
14 | / fn foo(&self, i: T) -> &Display {
15 | | &self[i]
16 | | }
| |_____^
note: ...so that the type `<C as std::ops::Index<T>>::Output` is not borrowed for too long
--> src/main.rs:15:9
|
15 | &self[i]
| ^^^^^^^^
error[E0311]: the associated type `<C as std::ops::Index<T>>::Output` may not live long enough
--> src/main.rs:15:9
|
15 | &self[i]
| ^^^^^^^^
|
= help: consider adding an explicit lifetime bound for `<C as std::ops::Index<T>>::Output`
note: the associated type `<C as std::ops::Index<T>>::Output` must be valid for the anonymous lifetime #1 defined on the method body at 14:5...
--> src/main.rs:14:5
|
14 | / fn foo(&self, i: T) -> &Display {
15 | | &self[i]
16 | | }
| |_____^
note: ...so that the type `<C as std::ops::Index<T>>::Output` will meet its required lifetime bounds
--> src/main.rs:15:9
|
15 | &self[i]
| ^^^^^^^^
error[E0311]: the associated type `<C as std::ops::Index<T>>::Output` may not live long enough
--> src/main.rs:15:10
|
15 | &self[i]
| ^^^^^^^
|
= help: consider adding an explicit lifetime bound for `<C as std::ops::Index<T>>::Output`
note: the associated type `<C as std::ops::Index<T>>::Output` must be valid for the anonymous lifetime #1 defined on the method body at 14:5...
--> src/main.rs:14:5
|
14 | / fn foo(&self, i: T) -> &Display {
15 | | &self[i]
16 | | }
| |_____^
note: ...so that the reference type `&<C as std::ops::Index<T>>::Output` does not outlive the data it points at
--> src/main.rs:15:10
|
15 | &self[i]
| ^^^^^^^
我根本不明白这个错误。 特别是因为错误讨论了C::Output
的生命周期(据我所知)与附加参数K
无关 。
有趣的是,没有返回特征对象&Display
,但是向返回的Foo
添加关联类型会使生命周期错误消失( Playground )。 但是,这对我来说不是解决方案。
这个错误是什么意思? 是否有意义? 这是编译器错误吗? 参数K
与C::Output
的生命周期有什么关系?
它是有道理的,并不是编译器错误,但它有点不方便。
可以为类型C
实现Index<T>
,使得C::Output
的类型必须比T
内部的生命周期长。 这是一个愚蠢的例子:
struct IntRef<'a>(&'a i32);
impl<'a, 'b: 'a> Index<IntRef<'a>> for IntRef<'b> {
type Output = IntRef<'a>;
fn index(&self, _: IntRef<'a>) -> &Self::Output {
self
}
}
毯子impl
将尝试为IntRef<'b>
实现Foo<IntRef<'a>>
,这是不健全的。 要了解原因,请查看此非编译示例:
let b = 2i32; // 'b begins here:
let b_ref = IntRef(&b);
let o: &Display; // a reference that outlives 'a but not 'b
{
let a = 1i32; // 'a begins here:
let a_ref = IntRef(&a);
o = &b_ref[a_ref]; // <-- this errors: "a doesn't live long enough"
// which is correct!
o = b_ref.foo(a_ref); // <-- this wouldn't error, because the returned
// value is `&'x (Display + 'x)` where 'x is
// the lifetime of `b_ref`
}
println!("{:?}", o);
o = &b_ref[a_ref];
将无法编译,因为实现了Index
,使得b_ref[a_ref]
不能超过a_ref
。 但是o = b_ref.foo(a_ref)
必须编译,因为Foo<T>::foo
的定义......
fn foo(&self, i: T) -> &Display // what you wrote
fn foo<'a>(&'a self, i: T) -> &'a ('a + Display) // what the compiler inferred
...强制输出的生命周期仅取决于&self
的生命周期(参见此问题 )。 编译器拒绝Foo
的全面实现,因为如果它被允许,你可以使用它来“扩大”生命周期,如上例中的a_ref
。
(我无法想出一种让IntRef
实用的方法,但事实仍然是你可以做到这一点。可能,内部可变性,一个足够聪明的人可能会引入不健全,这是允许的。)
只要求T
永远不会包含任何(非'static
)引用,您的工作就完成了。
impl<C, T> Foo<T> for C
where
T: 'static,
C: Index<T>,
C::Output: Display + Sized,
对于Index
trait的最常见用法可能就是这种情况,但是如果你想能够实现Foo<&T>
(这不是不合理的),你会想要尝试一些限制性较小的东西。
另一种可能性是要求C::Output
为'static
,但这又比必要时更保守。
让我们回到Foo::foo
的desugaring:
fn foo<'a>(&'a self, i: T) -> &'a ('a + Display)
注意两个'a
s in &'a ('a + Display)
。 虽然它们是相同的,但它们代表不同的东西:返回引用的(最大)生命周期,以及被引用的东西中包含的任何引用的(最大)生命周期。
在Index
,我们正在使用它来实现Foo
,返回的引用的生命周期总是与&self
的借用相关联。 但是Self::Output
可能包含具有不同(可能更短)生命周期的其他引用,这是整个问题。 所以我们真正想写的是......
fn foo(&self, i: T) -> &('a + Display) // what you write
fn foo<'b>(&'b self, i: T) -> &'b ('a + Display) // what the compiler infers
......将&self
生命与Self::Output
内部的任何生命周期分离开来。
当然现在的问题是'a
在任何地方都没有定义,所以我们必须将它作为参数添加:
trait Foo<'a, T> {
fn foo(&self, i: T) -> &('a + Display);
}
现在你可以告诉Rust C::Output
必须比'a
for the impl
to apply,并且一切都会好( 操场 ):
impl<'a, C, T> Foo<'a, T> for C
where
C: Index<T>,
C::Output: 'a + Display + Sized,
{
fn foo(&self, i: T) -> &('a + Display) {
&self[i]
}
}
解决方案1要求您向Foo
添加生命周期参数,这可能是不合需要的。 另一种可能性是向foo
添加一个where
子句,要求T
比返回的&Display
更长。
trait Foo<T> {
fn foo<'a>(&'a self, i: T) -> &'a Display where T: 'a;
}
它有点笨拙,但实际上它可以让你将需求转移到函数而不是特征本身。 缺点是这也通过坚持返回值永远不会超过T
任何引用来排除Foo
的某些实现。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.