Search code examples
rustrust-diesel

Overflow evaluating the requirement `_: Sized` when trying to use Diesel queries


I'm trying to loosely implement the repository pattern for an application I'm building, however I seem to have hit on some kind of recursive type definition by accident.

On the following code:

use diesel::{pg::Pg, Insertable, Queryable, QueryDsl, PgConnection, Identifiable, Table};
use std::marker::PhantomData;
use anyhow::anyhow;

pub struct Repository<Entity, SqlType, Tab> {
    _entity_phantom: PhantomData<Entity>,
    _type_phantom: PhantomData<SqlType>,
    table: Tab,
}

impl <Entity, SqlType, Tab> Repository<Entity, SqlType, Tab>
where
    Entity: Queryable<SqlType, Pg> + Insertable<Tab> + Identifiable,
    Tab: Table
{
    fn find_by_id(&self, id: i64, conn: &PgConnection) -> anyhow::Result<Entity> {
        match self.table.find(id).first::<Entity>(conn) {
            Ok(entity) => Ok(entity),
            Err(e) => Err(anyhow!("{}", e)),
        }
    }
}

I get the error:

error[E0275]: overflow evaluating the requirement `_: Sized`
  --> support/src/lib.rs:18:26
   |
18 |         match self.table.find(id).first::<Entity>(conn) {
   |                          ^^^^
   |
   = help: consider adding a `#![recursion_limit="256"]` attribute to your crate (`support`)
   = note: required because of the requirements on the impl of `FilterDsl<_>` for `<Tab as AsQuery>::Query`

I've tried increasing the recursion limit at the top of the crate, but to no avail. Sprinking + Sized all over the trait bounds, doesn't seem to do anything as well.

What am I missing?


Solution

  • Errors like these usually mean that diesel could not resolve your trait bounds, due to not being restrictive enough.

    Diesel generic programming is usually a bit more complex, but here's a few tips to help you the next time you get this error:

    • Whenever you use want to use a specific method like find, you have to make sure you also check for the same bounds!
    • Prefer to build specific re-usable pieces of functions that will allow you to get what you want
    • Every time you call a diesel method you are 'wrapping' a new statement, this new statement has to be bound or you will get weird error messages like in your question.
    • The result of these methods usually have a shorthand like Find or Limit in code below
    • As you can see on line 27, we have to nest Limit<Find<..>>, for every new method you call in the query chain, you will need to specify this.
    use anyhow::anyhow;
    use diesel::{
        associations::HasTable,
        dsl::{Find, Limit},
        query_dsl::{
            methods::{FindDsl, LimitDsl},
            LoadQuery,
        },
        PgConnection, RunQueryDsl, Table,
    };
    use std::marker::PhantomData;
    
    pub struct Repository<Entity, Tab> {
        _entity_phantom: PhantomData<Entity>,
        _table_phantom: PhantomData<Tab>,
    }
    
    impl<Entity, Tab> Repository<Entity, Tab>
    where
        Entity: HasTable<Table = Tab>,
        Tab: Table,
    {
        fn find_by_id(&self, id: i64, conn: &PgConnection) -> anyhow::Result<Entity>
        where
            Tab: LimitDsl + FindDsl<i64>,
            Find<Tab, i64>: LimitDsl + Table,
            Limit<Find<Tab, i64>>: LoadQuery<PgConnection, Entity>,
        {
            match Entity::table().find(id).first::<Entity>(conn) {
                Ok(entity) => Ok(entity),
                Err(e) => Err(anyhow!("{}", e)),
            }
        }
    }
    

    What am I missing?

    Generally one could guess what you were attempting to do, and I do not think it is your fault that you didn't arrive at the expected outcome. This kind of generic programming is hard to understand, and with all the types and their names T, U it is easy to get lost.

    • Your trait bound on Entity was wrong if I understood correctly what Entity refers to:
      • Queryable<Query, Database> is meant to signal that the type it is implemented on can be queried in Database through Query. It does not make sense to 'pass in' the type SqlQuery if you don't pass in the concrete type either!
    • Insertable<Tab> is, I assume, a left-over from other methods?
    • Identifiable as well?