Rust: Implementing From for postgres::rows::Row

Posted by Brahm Lower on Tue 15 May 2018

Table of Contents

Background

I'm working on a small project in Rust using a postgres backend. My results are read in from a database connection, and then used to construct instances of whatever type was read (rows from the player table are used to create instances of the Player struct).

While initially slapping code together, I used a rather simplistic function to "manually" handle the conversion from postgres::rows::Row to my Player:

fn row_to_db_player(row: Row) -> Player {
    Player {
        id:                 row.get(0),
        name:               row.get(1),
    }
}

pub fn fetch_all(conn: &Connection) -> Vec<Action> {
    let results = &conn.query(SQL_SELECT_ALL, &[]).unwrap();
    results.iter().map(row_to_db_action).collect()
}

This technically worked, but this is relatively poor design, especially when Rust offers a fancy trait system.

Enter std::convert::From

Problemo!

The documentation on this looks pretty straightforward, and really it is. In my particular case though, the nature of Row added some complications that took a while to parse through.

Given the documentation for the From trait, I came up with the following trait implementation (struct definition for full disclosure):

use postgres::rows::Row;

#[derive(Serialize, Deserialize, Debug)]
pub struct Player {
    pub id:   i32,
    pub name: String,
}

impl From<Row> for Player {
    fn from(row: Row) -> Self {
        Player {
            id:   row.get(0),
            name: row.get(1)
        }
    }
}

However that code was presenting me with the following error:

error[E0106]: missing lifetime specifier
  --> src/models/player.rs:12:11
   |
12 | impl From<Row> for Player {
   |           ^^^ expected lifetime parameter

I began to investigate...

alt text

Solution

I'd be lying if I said I solved this on my own, but I gave it a good shot. I found a StackOverflow post about this specific use-case (postgres Rows), and the solution offered this valuable insight:

The lifetime is on the reference to the Row, but not the Row itself.

-- Steve Klabnik

The solution quickly becomes apparent now, and obvious when looking back at the documentation for Row. The definition for Row, as provided by the documentation is:

pub struct Row<'a> { /* fields omitted */ }

The lifetime specified in this definition is what the compile error was complaining about. If we look only a few pixels further down the page, we can see how some traits had already been implemented for Row, giving us a helpful hint:

impl<'a> Row<'a> {
    ...
}

Sheesh. At least I know now.

Conclusion

To come full circle, my final working solution looks as follows:

impl<'a> From<Row<'a>> for Player {
    fn from(row: Row) -> Self {
        Player {
            id:   row.get(0),
            name: row.get(1)
        }
    }
}

Idealy, this sort of misunderstanding wouldn't trip me up for so long. In this case, I was confident I wasn't dealing with references, but the errors about lifetimes was casting doubt on that. This is the first I've had to consider references in about six years, since neither Python nor Haskell really expose you to them.

tags: rust