epsilonhalbe
epsilonhalbe

Reputation: 15967

Specialise type for polymorphic type

I'm learning rust (coming from a haskell background). And I am struggling to use polymorphic types - where in haskell I would use type holes/GHC's type inference to figure out what the return type of a function is I am stuck in rust, to specialise a generic parameter.

my current problem

Say I have a type describing a pomodoro-session (I store this data in a sqllite-database) and I want to display this data in a tui-table

use tui::widgets::Row;

pub struct Pom {
    pub id: i32,
    pub duration: i64,
    pub ticket_id: Option<String>,
    pub note: Option<String>,
}

impl Pom {
    pub fn as_row(self) -> ? {
        let ret : [String; 4] = [
            format!("{}", self.id),
            format!("{}", self.duration),
            self.ticket_id.unwrap_or(String::new()),
            self.note.unwrap_or(String::new()),
        ];
        Row::Data(ret.into_iter())
    }
}

The compiler is quite helpful, when I substitute ? with i32 (as suggested in (How do I print the type of a variable in Rust?), it tells me it expects something of type Row.

I know that Row is an enum with the following definition:

pub enum Row<D, I> where
    D: Iterator<Item = I>,
    I: Display,  {
    Data(D),
    StyledData(D, Style),
}

I've tried to insert incorrect types into the specialised type signature

pub fn as_row(self) -> Row<?,?> {

i.e. substitute the ? with i32 again the compiler tells me that i32 is not an iterator, but then I don't know how to further specialise.

For completeness sake

To make investigation easier here: https://github.com/epsilonhalbe/pomodorust/tree/4f8c4929201ed90ddaef8af8d21076dbde78782b is a link to the project on github that can be cloned and built. The errors I have posted here can be reproduced by modyfing src/database.rs to make compilation fail

Bonus:

If there was something equivalent to _ in haskell that could tell me what type the rust compiler is inferencing, then I could go pick apart the examples in the documentation

let row_style = Style::default().fg(Color::White);
Table::new(
        ["Col1", "Col2", "Col3"].into_iter(),
        vec![
            Row::StyledData(["Row11", "Row12", "Row13"].into_iter(), row_style),
            Row::StyledData(["Row21", "Row22", "Row23"].into_iter(), row_style),
            Row::StyledData(["Row31", "Row32", "Row33"].into_iter(), row_style),
            Row::Data(["Row41", "Row42", "Row43"].into_iter())
        ].into_iter()
    )
    .block(Block::default().title("Table"))
    .header_style(Style::default().fg(Color::Yellow))
    .widths(&[Constraint::Length(5), Constraint::Length(5), Constraint::Length(10)])
    .style(Style::default().fg(Color::White))
    .column_spacing(1);

Upvotes: 3

Views: 113

Answers (1)

sshashank124
sshashank124

Reputation: 32189

@kopecs is right. Row<std::slice::Iter<'a, String>, &'a String> would be the correct return type but as they explained, it would cause lifetime issues since it would be returning references to a local variable.

tui::widgets::Row requires its type D to be an Iterator. The issue with using an array and into_iter on the array is that IntoIterator is only "implemented for &[T; N] and &mut [T; N]" i.e. references. This is best explained by this Reddit thread

any such implementation needing to move elements out of an array, which leaves holes in the array, ... can cause Bad Things to happen if a panic happens during iteration

...

I believe Vec gets around this by leaking memory in the case of a panic

Anyways, one way to fix this is to just use Vec instead of arrays. Then, your iterator can own the String instead of only holding a reference to the String in the original array. The fixed method looks as follows:

pub fn as_row(self) -> Row<impl Iterator<Item=String>, String> {  // <-- correct return type
    let ret = vec![  // <-- use a Vec
        format!("{}", self.id),
        format!("{}", self.duration),
        self.ticket_id.unwrap_or(String::new()),
        self.note.unwrap_or(String::new()),
    ];
    Row::Data(ret.into_iter())
}

Upvotes: 4

Related Questions