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?
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:
find
, you have to make sure you also check for the same bounds!Find
or Limit
in code belowLimit<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.
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?