这是有意义的,不是编译器的bug,但有点不方便。
完整的解释
可以实现
Index<T>
对于一种类型
C
这样的话
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
Foo<IntRef<'a>>
对于
IntRef<'b>
,这是不健全的。要了解原因,请查看此非编译示例:
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
(见
this question
)编译器拒绝
Foo
因为如果允许的话,你可以用它来“扩大”像
阿雷夫
在上面的例子中。
(我想不出办法
IntRef
但事实是你
能够
去做吧。可能,由于内部的易变性,一个足够聪明的人可能会引入不健全,如果允许的话。)
解决方案0:快速更新
只需要那个
T
从不包含任何(非-
'static
)证明人和你的工作完成了。
impl<C, T> Foo<T> for C
where
T: 'static,
C: Index<T>,
C::Output: Display + Sized,
这可能是最常见的
索引
但如果你想实现
Foo<&T>
(这不是不合理的),你会想尝试一些不那么严格的方法。
另一种可能性是需要
C::输出
成为
'静态
但这又比必要的更保守。
解决方案1:最佳方法
让我们回到
Foo::foo
:
fn foo<'a>(&'a self, i: T) -> &'a ('a + Display)
注意这两个
'a
S在
&'a ('a + Display)
. 尽管它们是相同的,但它们代表不同的事物:被返回的引用的(最大)生存期,以及被引用事物中包含的任何引用的(最大)生存期。
在
索引
,这是我们用来实现的
福
,返回的引用的生存期始终与
&自己
。但是
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::输出
。
当然现在的问题是
A
特性中没有定义任何地方,因此我们必须将其添加为参数:
trait Foo<'a, T> {
fn foo(&self, i: T) -> &('a + Display);
}
现在你可以告诉铁锈了
C::输出
必须生存
A
对于
IMPL
申请,一切都会好起来的(
playground
):
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]
}
}
解决方案2:对方法进行绑定
解决方案1要求将生存期参数添加到
福
这可能是不可取的。另一种可能是添加
where
条款至
foo
这需要
T
比回来的人活得更久
&Display
。
trait Foo<T> {
fn foo<'a>(&'a self, i: T) -> &'a Display where T: 'a;
}
它有点笨拙,但实际上它让您将需求转移到功能上,而不是特性本身。缺点是
也
排除某些执行
福
通过坚持返回值在
T
.