Hi everyone.
I faced with an issue related to lifetimes:
error: lifetime may not live long enough
--> src/features/ui2/modal.rs:37:9
|
19 | impl<'a> Modal<'a> {
| -- lifetime `'a` defined here
...
25 | pub fn set_items<'b, S1, S2>(mut self, labels: &'b [S1], values: &'b [S2]) -> Self
| -- lifetime `'b` defined here
...
37 | self.lines = lines;
| ^^^^^^^^^^ assignment requires that `'b` must outlive `'a`
|
= help: consider adding the following bound: `'b: 'a`
and this is the code of my modal:
type NamedValue<'a> = (&'a str, &'a str);
#[derive(Default)]
pub struct Modal<'a> {
title: &'a str,
lines: Vec<Line<'a>>,
list_state: ListState,
}
impl<'a> Modal<'a> {
pub fn set_title(mut self, title: &'a str) -> Self {
self.title = title;
self
}
pub fn set_items<'b, S1, S2>(mut self, labels: &'b [S1], values: &'b [S2]) -> Self
where
S1: AsRef<str>,
S2: AsRef<str>,
{
let lines: Vec<Line> = Self::zip(&labels, &values)
.map(|row| {
let parts = row.collect::<Vec<_>>();
Self::build_line(parts)
})
.collect();
self.lines = lines;
self
}
pub fn render(&mut self, frame: &mut Frame, rect: Rect) {
let layout = &Layout::vertical(
[Constraint::Percentage(30), Constraint::Percentage(40), Constraint::Percentage(30)]
).split(rect);
let title = Line::from(
Span::raw(self.title).fg(Color::LightYellow).add_modifier(Modifier::BOLD)
);
let instructions = Line::from(vec![
Span::styled("Navigate: ", Style::new().add_modifier(Modifier::BOLD)),
Span::styled("<ArrowDn>", Style::new().fg(Color::Blue).add_modifier(Modifier::BOLD)),
Span::raw(" and "),
Span::styled("<ArrowUp>", Style::new().fg(Color::Blue).add_modifier(Modifier::BOLD)),
Span::styled(", select: ", Style::new().add_modifier(Modifier::BOLD)),
Span::styled("<Enter>", Style::new().fg(Color::Green).add_modifier(Modifier::BOLD)),
]);
let block = Block::bordered()
.title(title.centered())
.title_bottom(instructions.centered())
.border_set(border::ROUNDED);
let paragraph = Paragraph::new("").centered().block(block);
frame.render_widget(paragraph, layout[1]);
let list_items: Vec<ListItem> = self.lines
.iter()
.map(|l| ListItem::new(l.clone()))
.collect();
let list = List::new(list_items).highlight_symbol(">>>");
frame.render_stateful_widget(list, layout[1], self.list_state.borrow_mut());
}
fn build_line(parts: Vec<NamedValue>) -> Line {
let mut spans = Vec::new();
for (label, name) in parts.iter() {
spans.extend_from_slice(&[
Span::styled(
format!("{}: ", label),
Style::default().fg(Color::Magenta).add_modifier(Modifier::BOLD),
),
Span::styled(
format!("{}: ", name),
Style::default().fg(Color::Green),
)
]);
}
Line::from(spans)
}
fn zip<'b, S1, S2>(
labels: &'b [S1],
values: &'b [S2],
) -> impl Iterator<Item=impl Iterator<Item=NamedValue<'b>> + 'b>
where
S1: AsRef<str>,
S2: AsRef<str>,
{
values
.chunks(labels.len())
.map(move |chunk| {
chunk
.iter()
.zip(labels.iter())
.map(|(l, v)| (l.as_ref(), v.as_ref()))
})
}
}
I added 'b
lifetime here because I generate my modal in another part of the code and I pass the data by reference, so this data cannot outlive the modal:
Ok(input) = receiver.recv() => {
match input {
// ...
HandlerOutput::TransferCharactersList(characters) => {
let char_data = characters
.into_iter()
.flat_map(|c| vec![c.guid.to_string(), c.name])
.collect::<Vec<_>>();
*modal.lock().await = Modal::default()
.set_title("SELECT CHARACTER")
.set_items(&["Name", "Guid"], &char_data);
},
_ => {},
}
}
this modal is stored in mutex because I use it in two tasks: one task to fill the data and one task to render.
And I want to avoid creating extra String (which is obviously the simplest way to solve this issue).
Could somebody help me with fixing lifetimes issue ? And also what is the root cause - it is not clear for me, even if I try to set main lifetime to the items
this not help:
pub fn set_items<'b, S1, S2>(mut self, labels: &'b [S1], values: &'b [S2]) -> Self
where
S1: AsRef<str>,
S2: AsRef<str>,
{
// I try to set modal's lifetime here explicitly, but this NOT helped
let lines: Vec<Line<'a>> = ...;
self.lines = lines;
self
}
BTW If I do not use extra 'b
lifetime, I got this issue:
error[E0521]: borrowed data escapes outside of async block
--> src/features/ui2/mod.rs:72:37
|
41 | let mut modal = Arc::new(Mutex::new(Modal::default()));
| --------- `modal` declared here, outside of the async block body
...
72 | *modal.lock().await = Modal::default()
| ^^^^^^^^^^^^^^^^^^^ reference to `char_data` escapes the async block body here
73 | .set_title("SELECT CHARACTER")
74 | .set_items(&["Name", "Guid"], &char_data);
| ---------- borrow is only valid in the async block body