"parameter `B` is never used" and "expected value, found type parameter `B`" when implement trait with extra methods

I have some components implemented with tui-rs, all components implements trait UIComponent:

pub trait UIComponent<B: Backend> {
    fn new() -> Self where Self: Sized;
    fn render(&mut self, frame: &mut Frame<B>, rect: Rect);
}

this is my component, where I have an issue:

pub struct DebugPanel<'a> {
    items: Vec<Spans<'a>>,
}

impl<'a> DebugPanel<'a> {
    pub fn add_item(&mut self, output: LoggerOutput) -> &self {
        let message = Self::generate_message(output);

        if !message.content.is_empty() {
            self.items.push(Spans::from(message));
        }

        &self
    }

    fn generate_message(output: LoggerOutput) -> Span<'a> {
        match output {
            LoggerOutput::Info(data) if !data.is_empty() => Span::styled(
                format!("[INFO]: {}\n", data), Style::default().fg(Color::Gray)
            ),
            LoggerOutput::Debug(data) if !data.is_empty() => Span::styled(
                format!("[DEBUG]: {}\n", data), Style::default().fg(Color::DarkGray)
            ),
            LoggerOutput::Error(data) if !data.is_empty() => Span::styled(
                format!("[ERROR]: {}\n", data), Style::default().fg(Color::Red)
            ),
            LoggerOutput::Success(data) if !data.is_empty() => Span::styled(
                format!("[SUCCESS]: {}\n", data), Style::default().fg(Color::LightGreen)
            ),
            LoggerOutput::Server(data) if !data.is_empty() => Span::styled(
                format!("[SERVER]: {}\n", data), Style::default().fg(Color::LightMagenta)
            ),
            LoggerOutput::Client(data) if !data.is_empty() => Span::styled(
                format!("[CLIENT]: {}\n", data), Style::default().fg(Color::LightBlue)
            ),
            _ => Span::raw(""),
        }
    }
}

impl<'a, B: Backend> UIComponent<B> for DebugPanel<'a> {
    fn new() -> Self {
        Self {
            items: vec![],
        }
    }

    fn render(&mut self, frame: &mut Frame<B>, rect: Rect) {
        let block = Block::default()
            .title(PANEL_TITLE)
            .borders(Borders::ALL)
            .border_type(BorderType::Plain);

        let mut offset: usize = 0;
        if self.items.len() > rect.height as usize {
            offset = self.items.len() - (rect.height - MARGIN * 2) as usize;
        }

        let paragraph = Paragraph::new(self.items[offset..].to_vec())
            .alignment(Alignment::Left)
            .wrap(Wrap { trim: true })
            .style(Style::default().fg(Color::White).bg(Color::Black))
            .block(block);

        frame.render_widget(paragraph, rect);
    }
}

and this is UI renderer where I initialize my component:

pub struct UI<'a> {
    _terminal: Terminal<CrosstermBackend<Stdout>>,
    _debug_panel: DebugPanel<'a>,
    _title: Title,
}

impl<'a> UI<'a> {
    pub fn new() -> Self {
        enable_raw_mode().unwrap();
        let mut stdout = std::io::stdout();
        execute!(stdout, EnterAlternateScreen, EnableMouseCapture).unwrap();
        let backend = CrosstermBackend::new(stdout);
        let mut _terminal = Terminal::new(backend).unwrap();
        _terminal.clear().unwrap();
        _terminal.hide_cursor().unwrap();

        Self {
            _terminal,

            // components
            _debug_panel: DebugPanel::new(),
            _title: Title::new(),
        }
    }

    pub fn render(&mut self, options: UIOptions) {
        self._terminal.draw(|frame| {
            let chunks = Layout::default()
                .direction(Direction::Vertical)
                .margin(MARGIN)
                .constraints([
                    Constraint::Percentage(12),
                    Constraint::Percentage(76),
                    Constraint::Percentage(12),
                ])
                .split(frame.size());

            let output_panels = Layout::default()
                .direction(Direction::Horizontal)
                .constraints([
                    Constraint::Percentage(100),
                ])
                .split(chunks[1]);

            // ...

            self._title.render(frame, chunks[0]);
            self._debug_panel.render(frame, output_panels[0]);

        }).unwrap();
    }
}

the issue is that I cannot use this trait in case I want to implement some extra methods on my component. I got an error inside UI renderer:

error[E0282]: type annotations needed
  --> src\ui\mod.rs:57:27
   |
57 |             _debug_panel: DebugPanel::new(),
   |                           ^^^^^^^^^^^^^^^ cannot infer type for type parameter `B` declared on the trait `UIComponent`

it's not clear where I should add <B: Backend> to apply it for DebugPanel.

Below is what I want to achieve.
I would like to refactor the code to use custom generic Backend, so I can initialize UI like this:

let ui = UI::<Crossterm<Stdout>>::new();

so I tried to do this:

pub struct UI<'a, B: Backend> {
    _terminal: Terminal<B>,
    _debug_panel: DebugPanel<'a, B>,
    _title: Title,
}

impl<'a, B: Backend> UI<'a, B> {
    pub fn new() -> Self {
        enable_raw_mode().unwrap();
        let mut stdout = std::io::stdout();
        execute!(stdout, EnterAlternateScreen, EnableMouseCapture).unwrap();

        let mut _terminal = Terminal::new(B).unwrap();
        _terminal.clear().unwrap();
        _terminal.hide_cursor().unwrap();
    }
    // ...
}

pub struct DebugPanel<'a, B: Backend> {
    items: Vec<Spans<'a>>,
}

impl<'a, B: Backend> DebugPanel<'a, B> {
    pub fn add_item(&mut self, output: LoggerOutput) -> &self {
        let message = Self::generate_message(output);

        if !message.content.is_empty() {
            self.items.push(Spans::from(message));
        }

        &self
    }
    // ...
}

impl<'a, B: Backend> UIComponent<B> for DebugPanel<'a, B> {
    fn new() -> Self {
        Self {
            items: vec![],
        }
    }

    fn render(&mut self, frame: &mut Frame<B>, rect: Rect) {
        let block = Block::default()
            .title(PANEL_TITLE)
            .borders(Borders::ALL)
            .border_type(BorderType::Plain);

        let mut offset: usize = 0;
        if self.items.len() > rect.height as usize {
            offset = self.items.len() - (rect.height - MARGIN * 2) as usize;
        }

        let paragraph = Paragraph::new(self.items[offset..].to_vec())
            .alignment(Alignment::Left)
            .wrap(Wrap { trim: true })
            .style(Style::default().fg(Color::White).bg(Color::Black))
            .block(block);

        frame.render_widget(paragraph, rect);
    }
}

but then I got another error:

error[E0392]: parameter `B` is never used
  --> src\ui\debug_panel.rs:13:27
   |
13 | pub struct DebugPanel<'a, B: Backend> {
   |                           ^ unused parameter
   |
   = help: consider removing `B`, referring to it in a field, or using a marker such as `PhantomData`
   = help: if you intended `B` to be a const parameter, use `const B: usize` instead

and

error[E0423]: expected value, found type parameter `B`
  --> src\ui\mod.rs:49:43
   |
49 |         let mut _terminal = Terminal::new(B).unwrap();
   |                                           ^ not a value

unfortunately, I cannot add example on play-rust since there no tui-rs supported.

Could somebody help me to fix the typings issue ?

Issue with Backend type for UI initialization I solved by passing backend arg inside new():

pub struct UI<'a, B: Backend> {
    _terminal: Terminal<B>,
    _debug_panel: DebugPanel<'a>,
    _title: Title,
}

impl<'a, B: Backend> UI<'a, B> {
    pub fn new(backend: B) -> Self {
        enable_raw_mode().unwrap();
        execute!(std::io::stdout(), EnterAlternateScreen, EnableMouseCapture).unwrap();

        let mut _terminal = Terminal::new(backend).unwrap();
        _terminal.clear().unwrap();
        _terminal.hide_cursor().unwrap();

        Self {
            _terminal,

            // components
            _debug_panel: DebugPanel::new(),
            _title: Title::new(),
        }
    }

so I use it as:

let mut ui = UI::new(CrosstermBackend::new(std::io::stdout()));

but issue with DebugPanel still exists:

error[E0282]: type annotations needed
  --> src\ui\mod.rs:59:27
   |
59 |             _debug_panel: DebugPanel::new(),
   |                           ^^^^^^^^^^^^^^^ cannot infer type for type parameter `B` declared on the trait `UIComponent`

for now it's not clear how to correctly set type for it

Fixed! So, I moved <B: Backend> into render method:

pub trait UIComponent {
    fn new() -> Self where Self: Sized;
    fn render<B: Backend>(&mut self, frame: &mut Frame<B>, rect: Rect);
}

then just fixed impl for DebugPanel:

impl<'a> UIComponent for DebugPanel<'a> {
    fn new() -> Self {
        Self {
            items: vec![],
        }
    }

    fn render<B: Backend>(&mut self, frame: &mut Frame<B>, rect: Rect) {

thanks to all !

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.