Lifetime woes implementing ratatui::Widget for a reference

⚓ Rust    📅 2026-04-22    👤 surdeus    👁️ 1      

surdeus

I have this helper struct:

use ratatui::buffer::Buffer;
use ratatui::layout::{Alignment, Constraint, Direction, Layout, Rect};
use ratatui::prelude::{Style, Text, Widget};
use ratatui::widgets::Paragraph;

/// A table.
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Table<const COLS: usize, const ROWS: usize, T> {
    elements: [[T; COLS]; ROWS],
}

impl<const COLS: usize, const ROWS: usize, T> From<[[T; COLS]; ROWS]> for Table<COLS, ROWS, T> {
    fn from(elements: [[T; COLS]; ROWS]) -> Self {
        Self { elements }
    }
}

impl<'a, const COLS: usize, const ROWS: usize, T> Widget for &'a Table<COLS, ROWS, T>
where
    &'a T: Into<Text<'a>>,
{
    fn render(self, area: Rect, buf: &mut Buffer) {
        // Build 3 rows inside inner block
        let rows = Layout::default()
            .direction(Direction::Vertical)
            .constraints(
                [Constraint::Percentage(
                    u16::try_from(100usize / ROWS).expect("Percentage always fits."),
                ); ROWS],
            )
            .split(area);

        for (row_area, row) in rows.iter().zip(&self.elements) {
            let cols = Layout::default()
                .direction(Direction::Horizontal)
                .constraints(
                    [Constraint::Percentage(
                        u16::try_from(100usize / COLS).expect("Percentage always fits."),
                    ); COLS],
                )
                .split(*row_area);

            for (cell_area, cell) in cols.iter().zip(row) {
                let cell = Paragraph::new(cell)
                    .style(Style::default())
                    .alignment(Alignment::Center);

                cell.render(*cell_area, buf);
            }
        }
    }`
}

However, I cannot seem to get the lifetime constraints right on impl Widget:

   Compiling uml-tui v0.1.0 (/home/rne/Projekte/usb-missile-launcher/tui)
error[E0599]: the method `render` exists for struct `table::Table<3, 3, &str>`, but its trait bounds were not satisfied
   --> tui/src/app.rs:131:76
    |
131 |         Table::from([["", "^", ""], ["<", "<Enter>", ">"], ["", "v", ""]]).render(inner, buf);
    |                                                                            ^^^^^^ method cannot be called on `table::Table<3, 3, &str>` due to unsatisfied trait bounds
    |
   ::: tui/src/table.rs:8:1
    |
  8 | pub struct Table<const COLS: usize, const ROWS: usize, T> {
    | --------------------------------------------------------- method `render` not found for this struct
    |
note: trait bound `&&str: Into<Text<'_>>` was not satisfied
   --> tui/src/table.rs:20:12
    |
 18 | impl<'a, const COLS: usize, const ROWS: usize, T> Widget for &'a Table<COLS, ROWS, T>
    |                                                   ------     ------------------------
 19 | where
 20 |     &'a T: Into<Text<'a>>,
    |            ^^^^^^^^^^^^^^ unsatisfied trait bound introduced here
    = help: items from traits can only be used if the trait is implemented and in scope
    = note: the following traits define an item `render`, perhaps you need to implement one of them:
            candidate #1: `Widget`
            candidate #2: `ratatui::prelude::StatefulWidget`

For more information about this error, try `rustc --explain E0599`.
error: could not compile `uml-tui` (bin "uml-tui") due to 1 previous error

I also tried using HRTBs:


impl<'a, const COLS: usize, const ROWS: usize, T> Widget for &'a Table<COLS, ROWS, T>
where
    for<'b> &'b T: Into<Text<'a>>,
{
    fn render(self, area: Rect, buf: &mut Buffer) {
        // Build 3 rows inside inner block
        let rows = Layout::default()
            .direction(Direction::Vertical)
            .constraints(
                [Constraint::Percentage(
                    u16::try_from(100usize / ROWS).expect("Percentage always fits."),
                ); ROWS],
            )
            .split(area);

        for (row_area, row) in rows.iter().zip(&self.elements) {
            let cols = Layout::default()
                .direction(Direction::Horizontal)
                .constraints(
                    [Constraint::Percentage(
                        u16::try_from(100usize / COLS).expect("Percentage always fits."),
                    ); COLS],
                )
                .split(*row_area);

            for (cell_area, cell) in cols.iter().zip(row) {
                let cell = Paragraph::new(cell)
                    .style(Style::default())
                    .alignment(Alignment::Center);

                cell.render(*cell_area, buf);
            }
        }
    }
}

Resulting in:

   Compiling uml-tui v0.1.0 (/home/rne/Projekte/usb-missile-launcher/tui)
error[E0599]: the method `render` exists for struct `table::Table<3, 3, &str>`, but its trait bounds were not satisfied
   --> tui/src/app.rs:131:76
    |
131 |         Table::from([["", "^", ""], ["<", "<Enter>", ">"], ["", "v", ""]]).render(inner, buf);
    |                                                                            ^^^^^^ method cannot be called on `table::Table<3, 3, &str>` due to unsatisfied trait bounds
    |
   ::: tui/src/table.rs:8:1
    |
  8 | pub struct Table<const COLS: usize, const ROWS: usize, T> {
    | --------------------------------------------------------- method `render` not found for this struct
    |
note: trait bound `&'b &str: Into<Text<'_>>` was not satisfied
   --> tui/src/table.rs:20:20
    |
 18 | impl<'a, const COLS: usize, const ROWS: usize, T> Widget for &'a Table<COLS, ROWS, T>
    |                                                   ------     ------------------------
 19 | where
 20 |     for<'b> &'b T: Into<Text<'a>>,
    |                    ^^^^^^^^^^^^^^ unsatisfied trait bound introduced here
    = help: items from traits can only be used if the trait is implemented and in scope
    = note: the following traits define an item `render`, perhaps you need to implement one of them:
            candidate #1: `Widget`
            candidate #2: `ratatui::prelude::StatefulWidget`

For more information about this error, try `rustc --explain E0599`.
error: could not compile `uml-tui` (bin "uml-tui") due to 1 previous error

How can I convince the borrow checker, that this works for any type of reference?

10 posts - 3 participants

Read full topic

🏷️ Rust_feed