尝试使用 Diesel 查询时溢出评估要求 _: Sized

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

我正在尝试为我正在构建的应用程序松散地实现存储库模式,但是我似乎无意中遇到了某种递归类型定义。

关于以下代码:

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)),
        }
    }
}

我收到错误:

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`

我试过增加 crate 顶部的递归限制,但无济于事。在特征范围内洒 + Sized,似乎也没有做任何事情。

我错过了什么?

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?