1
votes

I am trying to make an Entity interface for dynamically mapping a database result into a Rust struct:

pub trait Entity {
    fn map(&self, Result<QueryResult>) -> Self;
}

pub struct DbQuery<T> {
    pub sql: String,
    pub params: Vec<Value>,
    pub limit: i32,
    pub paged: Option<Pagination>,
    pub entity: T,
}

pub struct Settings {
    pub name: String,
    pub value: Option<String>,
}

impl Entity for Settings {
    fn map(&self, result: Result<QueryResult>) -> Settings {
        // ...
        Settings {
            name: "hello".to_string(),
            value: None,
        }
    }
}

impl DbMapper {
    // ...

    pub fn find<T>(&self, query: DbQuery<T>) -> Option<Vec<T>> {
        println!("query find SQL: {}", query.sql);
        let mut stmt = &self.pool.prepare(query.sql).unwrap();
        let ret = Vec::new();
        for row in stmt.execute(query.params).unwrap() {
            ret.push(query.entity.map(row.unwrap()));

        }

        Some(ret)
    }
}

But I get an error:

error: no method named map found for type T in the current scope
ret.push(query.entity.map(row.unwrap())); |
note: the method map exists but the following trait bounds were not satisfied: T : std::iter::Iterator = help: items from traits can only be used if the trait is implemented and in scope; the following traits define an item map, perhaps you need to implement one of them: = help: candidate #1: models::holders::database::Entity = help: candidate #2: std::iter::Iterator

1
Your example doesn't quite stand on its own as a minimal example, but I think the answer would be that you haven't constrained T to implement Entity in DbQuery and/or DbMapper::find. - Chris Emerson
Sorry, I've use the minimal code to explain it. Unfortunately I've already tried with Entity instead of T but I receive the same error - plailopo
Hi - it's somewhat minimal but it's not complete enough to put into play.rust-lang.org and try to compile it and get the same error, so it's a bit hard to be sure. - Chris Emerson

1 Answers

3
votes

Here is a version of your code that runs on the playground and replicates your issue:

pub struct QueryResult;
pub struct Value;
pub struct Pagination;
pub struct DbMapper;

pub trait Entity {
    fn map(&self, Result<QueryResult, ()>) -> Self;
}

pub struct DbQuery<T> {
    pub sql: String,
    pub params: Vec<Value>,
    pub limit: i32,
    pub paged: Option<Pagination>,
    pub entity: T,
}

pub struct Settings {
    pub name: String,
    pub value: Option<String>,
}

impl Entity for Settings {
    fn map(&self, result: Result<QueryResult, ()>) -> Settings {
        // ...
        Settings {
            name: "hello".to_string(),
            value: None,
        }
    }
}

impl DbMapper {
    // ...

    pub fn find<T>(&self, query: DbQuery<T>) -> Option<Vec<T>> {
        println!("query find SQL: {}", query.sql);

        // ########## attempt to call map()
        let _ = query.entity.map(Ok(QueryResult {}));

        let ret = Vec::new();
        Some(ret)

    }
}

fn main() {}

The problem is that T in the DbQuery<T> argument in the find method has no idea that T is an Entity type. So we need to tell it:

pub fn find<T>(&self, query: DbQuery<T>) -> Option<Vec<T>>
    where T: Entity
{
    // ... code here ...
}

This now compiles and runs.

The compiler now knows that T is an Entity of some description, and it can call the map method on it.