Lifetime problems when using iced::Component

Hi there!

I am trying to use the trait iced::Component. I had a look a the example but my use case is not covered there.

When using the trait I have to implement view which has the type signature

    fn view(&self, state: &Self::State) -> Element<'_, Self::Event, Theme, Renderer>;

So, according to Rust’s lifetime elision rules, &selfand &Self::State are assigned two different lifetimes (e.g., 'a and 'b, respectively). The resulting Elementcan only contain data from &'a self, not from &'b Self::State.

Is this intentional? The resulting widgets can only reference data from self, not from Self::State?

If yes, how can I circumvent this limitation and use iced::Component correctly?

I haven’t tried the Component trait myself but you’re right, all the examples I’ve found (which are not that many by the way, just the one included in Iced and a couple more in Github) either don’t have an internal state or they use it during view but don’t need to return references to parts of it.

What exactly is your use case? What would be stored in your component’s internal state that you would need to reference directly from the final widget?

My component contains a canvas and a few buttons. The geometry drawn by my program on the canvas is cached. I am trying to make this cache part of the component’s local state because I do not need it to be part of the global application state.

The code looks something like this (simplified):

struct MyComponent<Message> {
    on_close: Box<dyn Fn() -> Message>,
}

struct MyComponentState {
    my_canvas_state: MyCanvasState
}

pub enum MyComponentEvent {
    MyCanvas(MyProgramMessage),
}

impl<Message> Component<Message> for MyComponent<Message> {
    type State = MyComponentState;
    type Event = MyComponentEvent;;

    fn update(&mut self, state: &mut Self::State, event: Self::Event) -> Option<Message> {
        //...
        None
    }

    fn view(&self, state: &Self::State) -> Element<Self::Event> {
        let element = Canvas::new(MyProgram {
            my_canvas_state: &my_canvas_state,
        })
        .into();

        element
            .map(MyComponentEvent::MyCanvas)
            .into()
    }
}

struct MyCanvasState {
    geometry_cache: iced::widget::canvas::Cache
}

struct MyProgram<'a>' {
    my_canvas_state: &'a MyCanvasState'
}

impl<'a> Program<MyProgramMessage> for MyProgram<'a> {
    //...
}

Hmm yes, this sounds like a perfectly valid use case. I don’t know what’s the proper way to fix this, or if maybe Iced hasn’t considered this scenario. It sounds like your options at the moment are: make the geometry part of the global app state, or clone the geometry from the internal state on every view.

I just wanted to bump this thread as I faced the same issue myself when implementing a widget for showing some live logs. In my case however I’m uncertain if the log messages should be stored in the “outer” or “inner” state. I would prefer the latter to have it fully encapsulated, but that would require cloning all messages on every call to view.

As is mentioned, all examples I have seen don’t use the state or don’t need to borrow from it. This leads me to belive that this might be an intentional API to ensure that such state should be “global”. However I suppose it might also just be an oversight in the API. Hopefully someone more involved could give us some clarity!