Struct enclosed ('self) lifetimes like for<'a> SomeTrait<'a> fo traits


#1

Hi,

recently I faced with a problem that I need to enclose the 'self lifetime inside the struct itself:

struct MyStruct<'self> { // but it should't be exposed
    data: Vec<Item>,
    id_map: HashMap<u32, &'self Item> // id_map it refer to some self.data's Item
}

enclosing lifetimes for traits is looks something like for<'a> MyTrait<'a>;

Proposal:

Implicilty add 'self to every struct, and propogate it to all inner structs:

struct MyStruct { // it will has <'self> implicitly
    inner: AnotherStruct, // it will has <'self> also implicitly
    other: &'self Item, // or ecplicitly
}

Any other suggestions to enclose 'self lifetime?


#2

I don’t think hidden lifetime parameters on structs are a good thing, for the reasons mentioned in this 4-year-old RFC: https://github.com/rust-lang/rfcs/blob/master/text/0141-lifetime-elision.md#lifetime-elision-in-structs

But extending '_ and elision to structs does sound interesting. Then you could have

struct MyStruct<'_> {
    inner: AnotherStruct<'_>, // Has a lifetime, but it's the same one
    outer: NoLifetimeHere, // No '_, so you know there's no lifetime
    other: &Item, // Same lifetime again, but like in fn elision you don't need the '_ explicitly
}

#3

This is an example of self-referential struct that is not supported in Rust. Not because of lack of annotations, but because it’s unsafe. Assignment to self.data = Vec::new() would create dangling pointers in self.id_map.


#4

Yep, as @kornel said, that’s unfortunately a rather inexpressible pattern in current Rust. You might find https://crates.io/crates/rental crate useful too solve your particular problem, but the solution won’t be pretty at all.


#5

@kornel, borrow checker shouln’d allow such assigment, actually nobody preventing to try to do it without such feature:

#[derive(Default)]
struct X<'s> {
    data: Vec<u32>,
    index: Vec<&'s u32>
}

fn main() { 
    let mut x = X::default();
    x.data.push(12);
    x.index.push(x.data.get(0).unwrap()); // ------ borrow of `x.data` occurs here
    x.data = Vec::new(); // ERROR: cannot assign to `x.data` because it is borrowed
}

#6

I’ve just got why self referencing structs are useless:

#[derive(Default)]
struct X<'s> {
    data: Vec<u32>,
    index: Vec<&'s u32>
}

fn main() { 
    let mut x = X::default();
    x.data.push(12);
    x.index.push(x.data.get(0).unwrap());
    x.data.push(13); // ERROR !!!
}

#7

But there is a case when it may be handy:

struct SomeStruct {
    bar: Bar
}

impl SomeStruct {
    pub fn foo(&'self, self) -> &'self Bar {
        &self.bar
    }
}

#8

That’s… just regular elision

impl SomeStruct {
    pub fn foo(&self) -> &Bar {
        &self.bar
    }
}

#9

what about if 'self can only be available to types that are const in memory for example

struct MyStruct {
    data: [i32; 8],
    id_map: HashMap<u32, &'self i32>
}

is it safe? is Pinned helping here? in what cases can I replace i32 with T?