Need help to specify type for iterator generic in rusqlite

Hi!

Just started with Rust a week ago, from a solid background in embedded C programming.
I would like to use the rusqlite crate (http://jgallagher.github.io/rusqlite/rusqlite/index.html) to write and read data from an sqlite3 db but immediately ran into some problems when trying to read from the db. This is my code:

extern crate rusqlite;

use rusqlite::Connection;

fn main() {
    let conn = Connection::open("db.sqlite3").unwrap();
    conn.execute("CREATE TABLE IF NOT EXISTS t1 (name TEXT PRIMARY KEY, nr INT)", &[]).unwrap();
    conn.execute("REPLACE INTO t1 VALUES (?, ?)", &[&"foo", &1]).unwrap();
    let mut stmt = conn.prepare("SELECT name FROM t1").unwrap();
    let rows = stmt.query_map(&[], |row| row.get(0));
}

When trying to build it I get:
src/main.rs:10:21: 10:30 error: unable to infer enough type information about _; type annotations or generic parameter binding required [E0282]
src/main.rs:10 let rows = stmt.query_map(&, |row| row.get(0));
^~~~~~~~~
src/main.rs:10:21: 10:30 help: run rustc --explain E0282 to see a detailed explanation
error: aborting due to previous error
error: Could not compile rusqlite-test.

So the call the error message refers to is query_map. I tried to manually set the type of the rows binding to what I expected to be the return typ of query_map, but utterly failed.

Looking into http://jgallagher.github.io/rusqlite/rusqlite/struct.Statement.html#method.query_map I tried to use many different qualifiers but to no avail.

Could anyone explain to me how to proceed with this?

Thanks is advance!

Row::get needs to know the type it gets. So for example row.get::<i64>(0). If they were some other annotations to know the return type of get, the annotation here won't be needed. Please also note that the query_map returns an iterator wrapped in a Result, so you'll need to add unwrap() + loop/collect() (like in examples) to get something usable.

Looks like I was too slow to respond :slight_smile:

Additional tip: the way I use Rusqlite is I map a row to a struct, this way I don't need to type explicit annotations since the compiler already has all the information it needs (thanks to the types of the fields). Also, with multiple rows I'm collecting into a Result<Vec<_>> so I don't have to manually unwrap every entry.

Additional explanation about how Row::get: Rusqlite knows how to convert a few common types such as i64, String, etc. between Rust and SQLite automatically, you can get the full list by looking at the implementations of the FromSql trait.

Thanks!

That fixed it, but I needed to make it row::get<i32, i64>(0). But I still don't really understand how the compiler uses that extra information to solve the original problem, where it complained about the query_map function? Is it because it can now deduce exactly what kind of iterator to return from query_map or is it because it now understands exactly how query_map should call row.get(0) to build the iterator?

Thank you!

I don't follow exactly how you do that mapping, but it sounds like a good idea. When it comes to the FromSql trait, I found out about it, and that was also where I realized I couldn't use an i64 for the number to retrieve.

Sorry I couldn't be more precise as I didn't have the relevant code nearby (this is at work).
See below for an excerpt of the relevant code:

  1. definition of the User structure
  2. definition of a list method that maps each row to an instance of the User structure, and collects all rows to a Result<Vec, DbError>.
use rusqlite::{Connection, Error as DbError, Row};

pub struct User {
    /// use an option so we can insert a user without an explicit id
    id: Option<i64>,
    first_name: String,
    last_name: String
    // other fields etc.
}

pub fn list(connection: &Connection) -> Result<Vec<User>, DbError> {
    let mut stmt = try!(connection.prepare("SELECT * FROM users"));
    let rows = try!(stmt.query_map(&[], |row|
        User {
            id: Some(row.get(0)),
            first_name: row.get(1),
            last_name: row.get(2)
        }
    ));
    rows.collect()
}

Even better in my opinion is to define a map function as part of the implementation of your structure when you need to do the mapping more than once: for instance listing all users and finding a specific user.

impl User {
    fn map(row: &Row) -> User {
        User {
            id: Some(row.get(0)),
            first_name: row.get(1),
            last_name: row.get(2)
        }
    }
}

You can then call query_map with the function as a parameter like this: query_map(parameters, User::map)

2 Likes

Oh right, I forgot about the index. row::get::<_, i64>(0) should be enough though.

Back to the original problem: The error message (which should be more specific, worth filing a bug) probably complains about the type of the closure. The closure's type is defined in a where clause:

where F: FnMut(&Row) -> T

Rust doesn't know which T to use, so it can't call query_map. By specifying the type of get, you also specify the return type of the closure.

So the answer is "both" (but the error message was only about the former).

Ahh, didn't think of that - this is very good help and advice! Takes some practice to get away from the C way of looking at things.

I see - that makes it clearer. Thanks again for pointing me in the right direction!