Help with ouroboros self-referential struct with external reference

⚓ Rust    📅 2025-11-13    👤 surdeus    👁️ 3      

surdeus

Hi,

I have the following code:

use ouroboros::self_referencing;

#[self_referencing]
struct Foo {
    backing_storage: Vec<u8>,

    #[borrows(mut backing_storage)]
    #[covariant]
    arena: Arena<'this>,

    #[borrows(arena)]
    #[not_covariant]
    model: Box<dyn DoStuff<'this> + 'this>,
}

struct Arena<'a> {
    memory: &'a mut [u8],
}

impl<'a> Arena<'a> {
    fn new(backing: &'a mut [u8], size: usize) -> Self {
        Arena {
            memory: &mut backing[0..size],
        }
    }
}

struct ArenaUser<'a, const N: usize> {
    arena: &'a Arena<'a>,
    some_data: [u8; N],
}

impl<'a, const N: usize> ArenaUser<'a, N> {
    fn new(arena: &'a Arena<'a>) -> Self {
        ArenaUser {
            arena,
            some_data: [0; N],
        }
    }
}

trait DoStuff<'a> {
    fn do_stuff(&self, arena: &'a Arena<'a>) -> &[u8];
}

impl<'a, const N: usize> DoStuff<'a> for ArenaUser<'a, N> {
    fn do_stuff(&self, arena: &'a Arena<'a>) -> &'a [u8] {
        &arena.memory[0..]
    }
}

fn main() {
    let model = FooBuilder {
        backing_storage: vec![0; 10],
        arena_builder: |backing| Arena::new(backing, 10),
        model_builder: |arena| Box::new(ArenaUser::<1>::new(arena)),
    }
    .build();
}

This currently builds. (It seems that ouroboros is not available on the playground so I can't share a link, sorry :().

This struct has an arena which takes a reference to a Vec stored in the same struct. It also has an object that takes a reference to the arena within itself. I'm storing that object as a trait object in order to erase the generics as I need the top struct to not be generic.

Now I need ArenaUser to hold a reference to an external object. I haven't been able to figure out the lifetimes to make this work. This is what I have now:

use ouroboros::self_referencing;

#[self_referencing]
struct Foo<'a> {
    backing_storage: Vec<u8>,

    #[borrows(mut backing_storage)]
    #[covariant]
    arena: Arena<'this>,

    #[borrows(arena)]
    #[not_covariant]
    model: Box<dyn DoStuff<'this> + 'a>,
}

struct Arena<'a> {
    memory: &'a mut [u8],
}

impl<'a> Arena<'a> {
    fn new(backing: &'a mut [u8], size: usize) -> Self {
        Arena {
            memory: &mut backing[0..size],
        }
    }
}

struct ArenaUser<'a, 'b, const N: usize> {
    arena: &'a Arena<'a>,
    some_data: [u8; N],
    reference: &'b u8,
}

impl<'a, 'b, const N: usize> ArenaUser<'a, 'b, N> {
    fn new(arena: &'a Arena<'a>, reference: &'b u8) -> Self {
        ArenaUser {
            arena,
            some_data: [0; N],
            reference,
        }
    }
}

trait DoStuff<'a> {
    fn do_stuff(&self, arena: &'a Arena<'a>) -> &[u8];
}

impl<'a, 'b, const N: usize> DoStuff<'a> for ArenaUser<'a, 'b, N> {
    fn do_stuff(&self, arena: &'a Arena<'a>) -> &'a [u8] {
        &arena.memory[0..]
    }
}

fn main() {
    let ref_value: u8 = 42;
    let model = FooBuilder {
        backing_storage: vec![0; 10],
        arena_builder: |backing| Arena::new(backing, 10),
        model_builder: |arena| Box::new(ArenaUser::<1>::new(arena, &ref_value)),
    }
    .build();
}

The code above just adds the reference field to ArenaUser and its new() method.

I've taken a look at the examples and it seems that what I want is possible, but the Box<dyn Trait<'a> + 'a> syntax is already getting too advanced Rust for me.

Can anyone point me in the right direction?

1 post - 1 participant

Read full topic

🏷️ Rust_feed