Follow

help requested. I am officially at wits end here.

Please take a look at my source file sdlstate.rs.

I'm getting a borrow checker error E0597 on line 39, indicating that tc does not live long enough. However, looking at the sdl2 sources (via its rust-docs), I don't see how my created texture, t, can possibly still refer to tc, which as far as I'm aware, is the only way this error can be generated under current conditions.

error[E0597]: `tc` does not live long enough
--> src/sdlstate.rs:39:21
|
14 | impl<'a> SdlState<'a> {
| -- lifetime `'a` defined here
...
39 | let mut t = tc
| _____________________^
40 | | .create_texture(
41 | | Some(sdl2::pixels::PixelFormatEnum::RGBA8888),
42 | | sdl2::render::TextureAccess::Streaming,
43 | | self.width,
44 | | self.height,
45 | | )
| |_____________^ borrowed value does not live long enough
...
49 | self.current_texture.set(Some(t));
| --------------------------------- argument requires that `tc` is borrowed for `'a`
...
56 | }
| - `tc` dropped here while still borrowed

Why is this happening? Why can't I re-arrange the code to prevent this from happening?

Right now, the only way this code will compile and run correctly is if I manually inject the re-paint code where I invoke f(), which utterly defeats the purpose and benefit of using closures in the first place.

In an attempt to fix this, I've tried:

  1. Replacing the Cell with RefCell.
  2. Removing Cell all-together and just using a raw Option type.
  3. Removing the 'a lifetime annotation.

None of these work, and almost always introduce some manner of errors on their own.

Please help. Thanks.

Also, I forgot to mention:

  1. I also tried passing t and tc explicitly as parameters to the PaintFn (making sure to update the signature, accordingly, of course).

@vertigo The `TextureCreator` is tied to the `Canvas`, and the textures it returns are tied to the `TextureCreator` (docs.rs/sdl2/latest/sdl2/rende, notice the `'_` lifetime)

this means that the struct is self-referential (i.e. one field is a reference to another), which don't work in Rust

@vertigo The 'a lifetime in your struct wants to be the lifetime of the `Canvas`. Do you see why the `TextureCreator` and its `Textures` are tied to each other?

@elomatreb

The 'a lifetime in your struct wants to be the lifetime of the Canvas.

I thought that the 'a was there to indicate that the current_texture lifetime would at least be as long as the SdlState structure's lifetime.

Do you see why the TextureCreator and its Textures are tied to each other?

No, I don't. According to docs.rs/sdl2/latest/src/sdl2/r and docs.rs/sdl2/latest/src/sdl2/r, there does not appear to be any relationship between them. I'm not sure where Rust is inferring a relationship from.

@vertigo That understanding of lifetime annotations is the wrong way around. They indicate that a struct annotated with one contains a reference to something else outside of itself, which naturally implies that the struct itself can only be valid as long as the thing it references is valid.

The types are tied to each other like this: `TextureCreator` is tied to the `Canvas`/the `Window` it contains because the definition of this method: docs.rs/sdl2/latest/sdl2/rende - notice how it carried along the `'s` lifetime annotation. In turn then the `Texture`s are tied to the `TextureCreator` because of the definition of the definition of this method: docs.rs/sdl2/latest/sdl2/rende - The `'_` thing in the return type is a shorthand for reusing the lifetime of a single input reference, i.e. the one of `&self` in this case

@elomatreb So, the call to create_texture is going to return a Texture whose lifetime is bounded by the TextureCreator (self). In this case, the lifetime is not explicit in the struct definitions, but in the method signatures. Is that a correct understanding?

Assuming that is correct, we can go one level backwards in the call-chain. The TextureCreator has lifetime 's, which is bounded by the Surface<'s> in the impl in which the method is defined.

So, the surface (which in my case is the window) must out-live the TextureCreator, which must out-live the Texture itself.

OK, going on the assumption that my understanding above is correct, then I'm still left without an understanding of what's causing the compiler error. tc definitely outlives t, and the canvas it's spawned from outlives tc.

@vertigo Each call to `paint_with` creates a fresh `TextureCreator`, which is dropped at the end of the function

@elomatreb Yes. That's the intention. But, inside the closure, there will be any number of calls to paste_stamp_be() or similar functions which draws directly into the texture.

I do not want to have to explicitly pass a texture argument for every time I call something to draw. So, I wanted to store the texture for future reference other methods.

@vertigo `t` is referencing `tc`, look at the `create_texture` function signature.
You need to store `tc` for a time >= `t`.

You are probably going to end up with a self-referential struct:
The field `self.t` will need to reference `self.tc`.
It's a bit hard, but it will solve your problem.

To get self-referential structs working, see this SO question stackoverflow.com/questions/32

@ranfdev I've looked at the signatures and the structs themselves, and I'm not finding where this reference is happening.

From another discussion on this topic, it looks like I might have misunderstood how lifetime annotations work; but, even after reevaluating structure lifetimes, I'm still not seeing why I'm getting the error.

I'll check the link you provided as soon as I'm in front of a computer again, thanks for the link. Hoping it will shed some light on my woes.

@vertigo `create_texture` takes `&self, ...` and returns a `Result<Texture<'_>,...>`

The lifetime `'_` refers to the lifetime of the only reference available, so `&self`.
In this case you can imagine `&self == &'_ self`

That means `t` is referencing `&tc`.

@ranfdev Right; but I can show in my code that tc out-lives t. What I can't get is why Rust doesn't think this is the case.

@vertigo in the code you posted, tc is _not_ outliving t. Tc gets dropped at the end of the function, while t keeps living inside the struct.

Rust sometimes is over aggressive, but not in this case.

The sdl2 crate authors designed the API in this way and rust is simply enforcing it, correctly

@ranfdev No; look after the call to the closure, where I take() the value of t again, and use it to clean up the texture. I fully expect t to be dropped at the end of the function, not to out-live it.

@ranfdev Also, let's leave the authors of the SDL2 crate out of this. I detect frustration levels seem to be rising for you, and I want to ensure you I'm not doing this to be annoying. My issues do not concern them, clearly. Let's just focus on my ignorance of what's happening.

I'm of course defending my code because (1) it helps me to clarify what I was thinking at the time, and (2) it provides you with that same insight which would help you find errors in the way I'm thinking about things. I'm not arguing to be prima donna here; I'm just trying to set a stage and clarify my thought processes as one would in any other academic setting.

I don't just want a cookie cutter solution that would fix my code; I already started with a working design, obviously. I want to understand why my refactor isn't working, so I can avoid these pitfalls again in the future, and not waste everyone's time again later on without good reason.

@ranfdev BTW, thanks again for that link. I wasn't aware that "move"-ing values actually did relocate values in memory. Thinking of lifetimes as "when things reside at an address" as distinct from "between allocation and dropping" is something I didn't know about, and this will help me going forward.

@vertigo Ah, I see...
You are right, t gets dropped at the end of the function

But rust doesn't care what you do with t after line 49. In 49 you are breaking the lifetimes guarantees, and that will trigger the error.

I can see the disappointment.

Sign in to participate in the conversation
hackers.town

A bunch of technomancers in the fediverse. This arcology is for all who wash up upon it's digital shore.