I have defined the following trait:
trait Read<T> {
fn next_row<'c>(&'c mut self) -> Result<std::option::Option<T>>;
}
(obviously 'c
would be elided but I just want to be able to refer to it later)
I have implemented this for a struct, ReaderA
, to return an owned value:
struct ReaderA {
data: Vec<Vec<u8>>
}
impl Read<Vec<u8>> for ReaderA {
fn next_row<'c>(&'c mut self) -> Result<std::option::Option<Vec<u8>>> {
Ok(self.data.pop())
}
}
Unsurprisingly, this works perfectly. The problem occurs when I try to implement this for some struct ReaderB
to return a borrowed value:
struct ReaderB {
data: Vec<Vec<u8>>
}
impl Read<&Vec<u8>> for ReaderB {
fn next_row<'c>(&'c mut self) -> Result<std::option::Option<&'c Vec<u8>>> {
Ok(self.data.first())
}
}
This doesn't work. The signature doesn't match the definition because the trait doesn't expect 'c
to be included in the return value. What am I doing wrong?
I could potentially add the lifetime as a trait generic parameter, however this feels unnecessary: I don't want to specify some other lifetime, I just want it to share the lifetime of the &mut self
borrow (I think). I also would far rather not have to include a lifetime in every implementation of the trait and PhantomData
in every struct, even when they don't return a borrowed value. I also don't want to run into something like this, but I don't really understand what I'm doing.
I feel this may be something to do with HRTB, however I can't work out how to get that to work.
I am somewhat lost with lifetimes, so please include as much detail as possible in your answer.
Two remarks:
std::iter::Iterator
.T
should have a reference to Self
or not. In one case it does, in the other it does not. This is the main problem why your approach fails; if somebody wants to use the trait, he could keep the produced value but destroy the Read
object, which should demonstrate why the ReaderB
implementation in your example code is rightfully forbidden.If you convert the generic to an associated type and annotate in the trait that Self
should always outlive the return value, then it works:
use anyhow::Result;
pub trait Read {
type Item<'a>
where
Self: 'a;
fn next_row(&mut self) -> Result<std::option::Option<Self::Item<'_>>>;
}
pub struct ReaderA {
data: Vec<Vec<u8>>,
}
impl Read for ReaderA {
type Item<'a> = Vec<u8>;
fn next_row(&mut self) -> Result<std::option::Option<Vec<u8>>> {
Ok(self.data.pop())
}
}
pub struct ReaderB {
data: Vec<Vec<u8>>,
}
impl Read for ReaderB {
type Item<'a> = &'a Vec<u8>;
fn next_row(&mut self) -> Result<std::option::Option<&Vec<u8>>> {
Ok(self.data.first())
}
}