iced-rs/iced

Toast example crahses if two or more toasts are removed at the same time

EmperialDev opened this issue · 1 comments

Is there an existing issue for this?

  • I have searched the existing issues.

Is this issue related to iced?

  • My hardware is compatible and my graphics drivers are up-to-date.

What happened?

If you add another toast in the new method and then run the example and wait 5 seconds it will crash, because the program tries to remove a toast at position 1, but there is only 1 toast left in the Vec, so the program panics.

How the new method should look to reproduce:

fn new(_flags: ()) -> (Self, Command<Message>) {
    (
        App {
            toasts: vec![
            Toast {
                title: "Example Toast".into(),
                body: "Add more toasts in the form below!".into(),
                status: Status::Primary,
            },
            Toast {
                title: "Example Toast 2".into(),
                body: "Add more toasts in the form below!".into(),
                status: Status::Primary,
            }],
            timeout_secs: toast::DEFAULT_TIMEOUT,
            ..Default::default()
        },
        Command::none(),
    )
}

What is the expected behavior?

That the program didn't panic and just removed both toast without a problem.

Version

master

Operating System

Windows

Do you have any log output?

thread 'main' panicked at examples\toast\src\main.rs:82:29:
removal index (is 1) should be < len (is 1)
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
error: process didn't exit successfully: `target\debug\toast.exe` (exit code: 101)

Maybe a solution to this crash, it seemes to fix the panics.

fn on_event( .. ) -> .. {
    if let Event::Window(_, window::Event::RedrawRequested(now)) = &event
    {
        let mut next_redraw: Option<window::RedrawRequest> = None;
        let mut removed_index = 0;    // Add this to keep track of the removed index

        self.instants.iter_mut().enumerate().for_each(
            |(index, maybe_instant)| {
                if let Some(instant) = maybe_instant.as_mut() {
                    let remaining =
                        Duration::from_secs(self.timeout_secs)
                            .saturating_sub(instant.elapsed());

                    if remaining == Duration::ZERO {
                        maybe_instant.take();
                        shell.publish((self.on_close)(index - removed_index));    // Adjust the toast to be removed, so the program wouldn't panic
                        removed_index += 1;    // Increment beacuse one toast was removed
                        next_redraw =
                            Some(window::RedrawRequest::NextFrame);
                    } else {
                        let redraw_at =
                            window::RedrawRequest::At(*now + remaining);
                        next_redraw = next_redraw
                            .map(|redraw| redraw.min(redraw_at))
                            .or(Some(redraw_at));
                    }
                }
            },
        );
        ..
}