I'm new to the language and still fighting the borrow checker. I've seen that some libraries use new() functions a.k.a. constructors without parameters and it works. Basically this means, that the returned data is being created inside new's function scope, and it's not deleted at the end of new's scope.
When trying this myself the borrow checker won't let this code through. How does one make this work, other than passing the i32 mutable reference as a parameter to the constructor.
Am I missing something?
#[derive(Debug)]
struct B<'a> {
b: &'a i32
}
#[derive(Debug)]
struct A<'a> {
one: B<'a>
}
impl<'a> A<'a> {
fn new() -> A<'a> {
// let mut b = 10i32;
A {
one: B{b: &mut 10i32}
}
}
}
fn main() {
let a = A::new();
println!("A -> {:?}", a);
}
The compiler error.
main.rs:15:19: 15:24 error: borrowed value does not live long enough
main.rs:15 one: B{b: &mut 10i32}
^~~~~
main.rs:12:20: 17:3 note: reference must be valid for the lifetime 'a as defined on the block at 12:19...
main.rs:12 fn new() -> A<'a> {
main.rs:13 // let mut b = 10i32;
main.rs:14 A {
main.rs:15 one: B{b: &mut 10i32}
main.rs:16 }
main.rs:17 }
main.rs:12:20: 17:3 note: ...but borrowed value is only valid for the block at 12:19
main.rs:12 fn new() -> A<'a> {
main.rs:13 // let mut b = 10i32;
main.rs:14 A {
main.rs:15 one: B{b: &mut 10i32}
main.rs:16 }
main.rs:17 }
error: aborting due to previous error
As per request, here's the practical example i'm trying to work with. There's this GUI library(Conrod), and it has some steps to instantiate it. Like in the example below.
let assets = find_folder::Search::ParentsThenKids(3, 3)
.for_folder("assets").unwrap();
let font_path = assets.join("fonts/NotoSans/NotoSans-Regular.ttf");
let theme = Theme::default();
let glyph_cache = GlyphCache::new(&font_path).unwrap();
let ui = &mut Ui::new(glyph_cache, theme);
My plan was to encapsulate the drawing of the app, into a struct. That would have a constructor and a few helper methods. For this to work i would have to have a field with a instance of the conrod::Ui<GlyphCache<'a>>
type, which is the type for the ui variable above.
I think adding things to main(i mean having all the allocations done in main), might not be the best way to do things.
let mut app_ui = app::AppUi::new(); // This would encapsulate all of the above configuration lines.
// use the ui here
for e in evets {
app_ui.handle_input();
app_ui.render();
}
Implementation of AppUi. It's not complete, but should show the general idea.
Just to make sure we're on the same page, the type conrod::Ui<GlyphCache<'a>>
requires a life lifetime parameter. And i want to have the same lifetime as the struct. The only way i know how to do that is to make the struct get a lifetime parameter itself, and pass it down to the UI type.
pub struct AppUi<'a> {
pub ui: conrod::Ui<GlyphCache<'a>>,
pub count: u16
}
impl<'a> AppUi<'a> {
pub fn new() -> AppUi<'a> {
let assets = find_folder::Search::ParentsThenKids(3, 3)
.for_folder("assets").unwrap();
let font_path = assets.join("FiraSans-Regular.ttf");
let theme = Theme::default();
let glyph_cache = GlyphCache::new(&font_path).unwrap();
AppUi {
ui: conrod::Ui::new(glyph_cache, theme),
count: 0
}
}
}
=======================
The solution i went with, and it worked in the end(at least it works for now). Was to create a helper function, that would return a glyph_cache and just used that. I'm not sure if it's idiomatic Rust, will just use it for now. Should probably get used to working with the borrow checker.
pub struct AppUi<'a> {
pub ui: conrod::Ui<GlyphCache<'a>>,
pub count: u16
}
impl<'a> AppUi<'a> {
pub fn new() -> AppUi<'a> {
AppUi {
ui: conrod::Ui::new(GlyphCache::new(&get_default_font_path()).unwrap(), Theme::default()),
count: 0
}
}
}
pub fn get_default_font_path() -> PathBuf {
find_folder::Search::ParentsThenKids(3, 3)
.for_folder("assets")
.unwrap()
.join("FiraSans-Regular.ttf")
}