Combobox on_selected closure capture restrictions

Hi,

I want my combobox to return a 2 strings, so I know which combo on the ui was used (I have a lot of them).

So when creating the combobox, I try and have the closure capture a string which I can return from on_selected, similar to below,

for i in &v.state {
  let c = combo_box(&i.combo_state, "", Some(&i.value),move |x| Message::ComboSelected(v.name.clone(), x));
  site_container = site_container.push(c);
}

I get an error that goes:

for v in s {
    |                          ^
    |                          |
    |                          `state` escapes the function body here
    |                          argument requires that `'1` must outlive `'static`

If I try a similar thing with text(), it works, similar to below:

for (k,v) in &i.codes_map {
   let t = text_input(k, v).on_input(move |s| Message::NightCodeChanged(n,n2,k.to_string(),s));
  items_row = items_row.push(t);
}

If I look at the docs for combobox on_selected versus textinput’s on_input, I see the following:

combobox new function has:

on_selected: impl Fn(T) → Message + 'static,

And textinput on_input has:

on_input: impl Fn(String) → Message + 'a,

I’m guessing the 'static is the issue on combobox? Why would combobox need 'static lifetimes and how can I get around this I wonder?

If I try and move the clone() out of the closure with below:

for i in &v.state {
  let n = v.name.clone();
    let c = combo_box(&i.combo_state, "", Some(&i.value),
                            move |x| Message::ComboSelected(n, x));
  site_container = site_container.push(c);
}

The error I then get looks like:

292 |                         let n = v.name.clone();
    |                             - captured outer variable
293 |                         let c = combo_box(&i.combo_state, "", Some(&i.value),
294 |                             move |x| Message::ComboSelected2(0, 0, n, x));
    |                             -------- captured by this `Fn` closure ^ move occurs because `n` has type `std::string::String`, which does not implement the `Copy` trait

Any advice or suggestions would be appreciated.
Thanks

No an expert in lifetimes myself, but an SSCCE would help reproduce the problem and experiment some solutions.

In the meantime, as an alternative solution, can you use different messages for your different comboboxes? Like Combo1Selected and Combo2Selected…

1 Like

Thanks, not quite sure where to paste a SSCCE, rust playground doesn’t find iced. If you suggest a better one I will paste it there.

So the above works, but if I uncomment the combo_box part, it fails. This seems like the smallest workable example I was able to come up with.

This seems to work.

    for i in &state.combo_states {
        let name = i.name.clone();
        let c = combo_box(
            &i.state,
            &i.name,
            None,
            move|x| Message::ComboSelected(name.clone(), x),
        );
        row = row.push(c);
    }

I’m not entirely sure why, but I think it’s because:

  1. The closure can’t keep non-static references because of the static trait bound imposed by the combobox on_selected parameter, so we clone the value to its own variable in let name = i.name.clone(); and pass that (we move it actually) to the closure instead.
  2. The closure can’t return the value it now owns because it would only be able to return it once, and this is not a FnOnce, so every time it returns it it needs to be cloned again.

So it’s a lot of cloning. If you could use different message options like I suggested in my previous message, maybe that would be more efficient.

1 Like

Wow thanks! I didn’t think about trying a double clone. Your explanation sounds right, appreciate it. I have tens of combo boxes and “add” button to add more, so different messages for each is not an option.
In the meantime I’ve opted to use enumerate() and pass a usize in each message, so ComboSelected(usize, String) type message so I know the index of which combo is selected. Since usize is Copy from enumerate(), the closure is happy about it.
Any idea on why combobox on_selected is requiring a 'static lifetime? I’m just curious about it.
Thanks for your time and help on this issue.

Using a usize sounds even easier, good thinking.

I don’t know why on_selected has that restriction. To me it’s not that different from on_input, so they should have similar requirements. I would even suggest that you try removing that requirement from the widget yourself and see if you can make iced compile. If you make it work you could even send a PR, or if you don’t maybe you understand why it needs it.

1 Like