Hi all,
I recently ran into this tiny problem:
static DUMMY: [u8; 30] = [0; 30];
// Lets assume we have a TraitB that serves content with lifetime 'data
trait TraitB<'data> {
fn get_some_buffer(self: &Self) -> &'data [u8];
}
// Lets further assume there is a TraitA that can serve TraitBs via a closure
trait TraitA<'data> {
fn foreach_b(self: &Self, f: &mut FnMut(&TraitB<'data>) -> ()) -> ();
}
// Implementation 1 now implements these traits as some kind of proxy.
// So 'data is, in fact, an external lifetime and the implementation 1 just serves as proxy.
#[allow(dead_code)]
struct Impl1B<'data> {
data: &'data [u8]
}
#[allow(dead_code)]
struct Impl1A {}
impl<'data> TraitB<'data> for Impl1B<'data> {
fn get_some_buffer(self: &Self) -> &'data [u8] {
self.data
}
}
impl<'data> TraitA<'data> for Impl1A {
fn foreach_b(self: &Self, f: &mut FnMut(&TraitB<'data>) -> ()) -> () {
let tmp = Impl1B { data: &DUMMY };
f(&tmp);
}
}
// Implementation 2, on the other hand, is the actual owner of the data.
// This works, but has the unpleasent side effect that I always need a borrowed Impl2A.
// (see line 59)
#[allow(dead_code)]
struct Impl2B {
data: Vec<u8>
}
#[allow(dead_code)]
struct Impl2A {
bs: Vec<Impl2B>
}
impl<'data> TraitB<'data> for &'data Impl2B {
fn get_some_buffer(self: &Self) -> &'data [u8] {
&self.data
}
}
impl<'data> TraitA<'data> for &'data Impl2A {
fn foreach_b(self: &Self, f: &mut FnMut(&TraitB<'data>) -> ()) -> () {
f(&&self.bs[0]);
// ^ Why do I need this double borrow here?
// Why can't I just write &self.bs[0]?
// XXX
}
}
// XXX
// ... Or is there even a smarter way to implement Impl2?
// ... because Impl3 does not seem to work (see line 82)
#[allow(dead_code)]
struct Impl3B {
data: Vec<u8>
}
#[allow(dead_code)]
struct Impl3A {
bs: Vec<Impl3B>
}
impl<'data> TraitB<'data> for Impl3B {
fn get_some_buffer(self: &Self) -> &'data [u8] {
// &self.data
// ^ not allowed as 'data is just unrelated
// I cannot reference the lifetime of self, can I?
// "cannot infer an appropriate lifetime for borrow expression due to conflicting requirements"
&DUMMY
}
}
impl<'data> TraitA<'data> for Impl3A {
fn foreach_b(self: &Self, f: &mut FnMut(&TraitB<'data>) -> ()) -> () {
f(&self.bs[0]);
}
}
Errors:
Compiling playground v0.0.1 (/playground)
Finished dev [unoptimized + debuginfo] target(s) in 0.40s
I basically use 2 traits to abstract some data access.
The first trait implementation is not the actual owner of the data, while the second one is.
So while I'm satisfied with Impl1, Impl2 is just not very convenient to use as it is defined on the borrowed struct.
I now asked myself whether it is actually needed to implement the traits on borrowed structs?
Impl3 is not working due to the unrelated lifetime 'data in line 82.
Do I miss something here?
Is there a better way to model something like this?
Thanks