Need help with an chicken & egg problem / self referencing on using nanovg-rs

Hi guys,

I try a lot of stuff and googling. Now I begin to get headache.
currently I have no idea how to get this code snippet to run.

The problem is that I can't change the way the Image and Context structures work because they came from the nanovg-rs create.
But I also want to use my Gui Struct to avoid loading a image on each frame rendering function call :wink:

use std::ptr;

// begin [fixed came from nanovg-rs crate]
struct Context(*mut u32);
impl Context {
    fn new() -> Context {
        Context {
            0: ptr::null_mut(),
        }
    }
}

struct Image<'a>(&'a Context, u32);
impl<'a> Image<'a> {
    fn new(ctx: &'a Context) -> Image<'a> {
        Image {
            0: ctx,
            1: 500,
        }
    }
}
// end [fixed came from nanovg-rs crate]

// trying to use a GuiBuilder...
struct GuiBuilder {
    context: Context,
}

impl GuiBuilder {
    fn new(context: Context) -> Self {
        Self {
            context
        }
    }
    
    pub fn build<'a>(self) -> Gui<'a> {
        
        //load some images 
        let image = Image::new(&self.context);
        
        Gui {
            ctx: self.context,
            img: image,
        }
    }
}



struct Gui<'a> {
    ctx: Context, //   -
    img: Image<'a>, // - consumes a reference of context... is like a self referencing
}



impl <'a> Gui<'a> {
    pub fn new() -> GuiBuilder {
        let context = Context::new();
        GuiBuilder::new(context)
    }
    
    pub fn some_test_output(&self) {
        println!("hallo");
    }
}

fn main() {
    let testing = Gui::new().build();
    
    testing.some_test_output()
}

(Playground)

Here my next try to get rid of my problem:

use std::ptr;
use std::marker::PhantomData;
use std::ops::Deref;
use std::rc::Rc;

// begin [fixed came from nanovg-rs crate]
struct Context(*mut u32);
impl Context {
    fn new() -> Context {
        Context {
            0: ptr::null_mut(),
        }
    }
}

struct Image<'a>(&'a Context, u32);
impl<'a> Image<'a> {
    fn new(ctx: &'a Context) -> Image<'a> {
        Image {
            0: ctx,
            1: 500,
        }
    }
}
// end [fixed came from nanovg-rs crate]

// trying to use a GuiBuilder...
struct GuiBuilder<'a> {
    context: ContextHolder<'a>,
    
}

impl<'a> GuiBuilder<'a> {
    fn new(context: ContextHolder<'a>) -> Self {
        Self {
            context
        }
    }
    
    pub fn build(self) -> Gui<'a> {
        
        //load some images 
        //let image = Image::new(self.context.get());
        
        Gui {
            ctx: self.context,
            img: None,
        }
    }
}

struct ContextHolder<'a> {
    inner: Rc<Context>,
    phantom: PhantomData<&'a ()>,
}
impl<'a> ContextHolder<'a> {
    fn new(context: Context) -> ContextHolder<'a> {
        ContextHolder {
            inner: Rc::new(context),
            phantom: PhantomData,
        }
    }

}
impl<'a> Deref for ContextHolder<'a> {
    type Target = Context;
    fn deref(&self) -> &Context {
        &self.inner
    }
}

struct Gui<'a> {
    ctx: ContextHolder<'a>, //   -
    img: Option<Image<'a>>, // - consumes a reference of context... is like a self referencing
}



impl <'a> Gui<'a> {
    pub fn new() -> GuiBuilder<'a> {
        let nanovg_context = Context::new();
        let context = ContextHolder::new(nanovg_context);
        GuiBuilder::new(context)
    }
    
    pub fn some_test_output(&self) {
        println!("hallo");
    }
    
    pub fn load_some_data(&'a mut self) {
        let image = Image::new(&self.ctx);
        
        self.img = Some(image);
    }
}

struct App<'a> {
    app_name: String,
    gui: Gui<'a>,
}

impl<'a> App<'a> {
    pub fn new() -> App<'a> {
        let mut gui = Gui::new().build();
        gui.load_some_data();

        App {
            app_name: "Wooow".to_string(),
            gui,
        }
    }
    
    pub fn output_some_gui(&self) {
        self.gui.some_test_output();
    }
}

fn main() {
    
    let a = App::new();
    
    a.output_some_gui();
    
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0515]: cannot return value referencing local variable `gui`
   --> src/main.rs:107:9
    |
105 |           gui.load_some_data();
    |           --- `gui` is borrowed here
106 | 
107 | /         App {
108 | |             app_name: "Wooow".to_string(),
109 | |             gui,
110 | |         }
    | |_________^ returns a value referencing data owned by the current function

error[E0505]: cannot move out of `gui` because it is borrowed
   --> src/main.rs:109:13
    |
102 |   impl<'a> App<'a> {
    |        -- lifetime `'a` defined here
...
105 |           gui.load_some_data();
    |           --- borrow of `gui` occurs here
106 | 
107 | /         App {
108 | |             app_name: "Wooow".to_string(),
109 | |             gui,
    | |             ^^^ move out of `gui` occurs here
110 | |         }
    | |_________- returning this value requires that `gui` is borrowed for `'a`

error: aborting due to 2 previous errors

Some errors have detailed explanations: E0505, E0515.
For more information about an error, try `rustc --explain E0505`.
error: Could not compile `playground`.

To learn more, run the command again with --verbose.

The issue is if Gui is moved then its self-reference will dangle. There are multiple ways to attack this problem, but one way is not owning the referenced value but requires the caller to own the value.
So the types will be:

struct GuiBuilder<'a> {
    context: &'a Context,
}

struct Gui<'a> {
    ctx: &'a Context,
    img: Image<'a>,
}

and implement GuiBuilder::new(context: &'a Context) -> GuiBuilder<'a>.
A caller then can pass their own Context:

fn main() {
    let context = Context::new();
    let gui = GuiBuilder::new(&context).build();
    gui.some_test_output();
}

If you don't want to take arbitrary Context, you can wrap it in a new type.

Thanks @pcpthm!
you save my day! :slight_smile:

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.