Lost in the lifetimes universe, I guess

Hello. So before I start explaining the problem I would like to let you know that I am lost here :sweat_smile:

I suppose I have a problem with lifetimes within the same struct but I am also wondering if this is not simply a bad design I did which cause the problem.

I have a struct https://github.com/cedeber/fluss/blob/a317abf7db4d516a05902dea39e34faeb92aade8/src/app/main.rs#L21 which represents the global state of the whole App (done with yew)

pub struct AppModel {
    link: ComponentLink<Self>,
[..]
    context: Option<&'static mut WebRenderContext<'static>>,
}

The ComponontLink forces me to have static lifetime and the WebRenderContext wants to have lifetimes too, so this is the only way I see to have them both here. I read it could help if I split this struct into peaces but alls structs look like to need the same lifetime.

Later I fill a UIState struc with

pub struct UiState<'a> {
    // pub context: &'a CanvasRenderingContext2d,
    pub context: &'a mut WebRenderContext<'a>,
    pub cursor: &'a Box<Cursor>,
    pub canvas_size: &'a (f64, f64), // use RectGeometry? Not needed anymore?
    pub hover_widget_uuid: &'a Option<String>,
    pub select_widget_uuid: &'a Option<String>,
}

When I reuse the UiState later I've got conflicts with lifetimes. The main problem is in the AppModel::paint function in src/app/main.rs.

Again, to be honest I have the feeling I doing something wrong, but I can't find what and/or how to solve it. If you need access to the whole code, it is accessible here: https://github.com/cedeber/fluss/tree/cedeber/issue4

Thanks a lot for your help.

Struct with borrowed references in its fields are definitely advanced topic in Rust. You should not write such code until the whole lifetime system feels natural to you.

I checked your code a bit, specifically the line which initializes Option<&'static mut WebRenderContext<'static>>. But honestly, does it compiles? The only safe way to get &'static mut T is to leak the Box<T> via Box::leak() function. This is safe as nobody can reclaim that allocation in safe way. But in your code

self.context = Some(&mut WebRenderContext::new(...));

The WebRenderContext value is created at this line, but nobody has taken its ownership. So it will be dropped at the end of the enclosing statement. It means the field context would contains dangling reference after this line. But the Rust is a safe language as it never allows dangling reference so the compiler rejects to compile it.

Conclusion? As I stated at the top, do not store references within your struct. References are borrowing of values which are owned by someone else. The compiler check your code with the lifetime system, and throws compile error if it finds some chance of dangling reference, even ones seems too tiny for human eyes. But believe me, it's infinitely better than the weeks or months of micro-debugging on moderate sized C++ project to fix memory error which only crashes your server on midnight Friday.

Haha, of course it doesn't compile :wink: At least you confirm that the self.context assignment is wrong.
Looks like I have to think differently about how to share the canvas context between widgets. (and AppModel/UiState then too)

'static is a very special lifetime in Rust. In practice it means something is either built-in into the program at compile time (like string literals or global constants) or it's a leaked memory.

When the compiler tells you something has to live for a static lifetime, you should not follow its advice literally and add 'static annotation, because it's very unlikely to help: you can't make complex objects into built-in constants, and you don't want to leak memory!

So when you see compiler demanding 'static, read it as "DO NOT USE REFERENCES". Things that are marked 'static in practice forbid use of any kind of temporary reference. In that case you must abandon code that uses &something, and change it to owned objects, e.g. WebRenderContext or Arc<WebRenderContext> instead.

1 Like

Looks like I need to do Option<WebRenderContext<'static>> because then it complains that a normal lifetime won't outlive the static lifetime of ComponentLink, in the same AppModel struct.

Then I face the problem that I can't have access to the WebRenderContext as a mutable ref because it doesn't have been declared as mutable. And, in addition, the WebRenderContext does not implement the Copy trait.

I got access to it here:

let context = match self.context {
    Some(it) => it,
    None => return,
};

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