Reputation: 18316
I have a struct that wraps a std::cell::Ref
and provides access by reference to the underlying value. Something like this:
use std::cell::Ref;
struct IntAccess<'a> {
i: Ref<'a, i32>,
}
impl IntAccess<'_> {
fn get(&self) -> &i32 {
&self.i
}
}
This works fine. Since I have multiple structs like this, I'd like to define a common trait:
trait Access {
type Item;
fn get(&self) -> Self::Item;
}
However, I get into trouble when trying to implement Access
for IntAccess
:
impl<'a> Access for IntAccess<'a> {
type Item = &'a i32;
fn get(&self) -> Self::Item {
&self.i
}
}
It fails with the following error:
error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
--> src/main.rs:23:9
|
23 | &self.i
| ^^^^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime defined here...
--> src/main.rs:22:12
|
22 | fn get(&self) -> Self::Item {
| ^^^^^
note: ...so that reference does not outlive borrowed content
--> src/main.rs:23:9
|
23 | &self.i
| ^^^^^^^
note: but, the lifetime must be valid for the lifetime `'a` as defined here...
--> src/main.rs:19:6
|
19 | impl<'a> Access for IntAccess<'a> {
| ^^
note: ...so that the types are compatible
--> src/main.rs:22:33
|
22 | fn get(&self) -> Self::Item {
| _________________________________^
23 | | &self.i
24 | | }
| |_____^
= note: expected `<IntAccess<'a> as Access>`
found `<IntAccess<'_> as Access>`
I think I kind of get what the compiler is trying to tell me: I'm trying to borrow self.i
which has an anonymous lifetime unrelated to the lifetime of Self::Item
('a
). So what seems to be needed is to somehow tie the lifetime of self
in get
to the lifetime 'a
. Is there a way to do this?
I have to admit that I don't fully grasp the problem. Since I pass the lifetime 'a
to the Ref
, and Ref
internally stores a reference with this lifetime, it seems to me that it should somehow be possible to get a reference with lifetime 'a
out of it without having to explicitly tie self
to this lifetime as well. So what am I missing here?
Upvotes: 4
Views: 1240
Reputation: 70900
Your understanding is correct. About your doubt, let's name the lifetimes for easier debugging:
impl<'a> Access for IntAccess<'a> {
type Item = &'a i32;
fn get<'b>(&'b self) -> Self::Item {
&self.i
}
}
self
has the type &'b IntAccess<'a>
. 'b
is shorter than or equal to 'a
- that is required for self
to be well-formed, i.e. able to exist (otherwise, it would contain a dangling reference).
For that reason, we cannot borrow self
for more than 'b
- or we could do something like:
let v: IntAccess<'a>;
let inner: &'a i32 = {
let r: &'b IntAccess<'a> = &v;
<IntAccess<'a> as Access>::get(r)
}
let mut_r: &mut IntAccess<'a> = &mut v;
And we have both a shared and a mutable reference to (parts of) inner
!
Your problem cannot be solved fully without Generic Associated Types. They allow you to parameterize an associated type by a lifetime, making it able to express "I want to return this associated type tied to the lifetime of self
":
#![feature(generic_associated_types)]
trait Access {
type Item<'a>
where
Self: 'a;
fn get<'a>(&'a self) -> Self::Item<'a>;
// Or, with lifetime elision:
// fn get(&self) -> Self::Item<'_>;
}
impl<'a> Access for IntAccess<'a> {
type Item<'b> = &'b i32
where
'a: 'b;
fn get<'b>(&'b self) -> Self::Item<'b> {
&self.i
}
}
Can we do that on stable? We can emulate that. Instead of implementing Access
for IntAccess
itself, we will implement it for a reference to it!
trait Access {
type Item;
fn get(self) -> Self::Item;
}
impl<'a, 'b> Access for &'b IntAccess<'a> {
type Item = &'b i32;
fn get(self) -> &'b i32 {
&self.i
}
}
This does not always work, and thus is not a full replacement to GATs, but is good enough in this case.
Upvotes: 4