Diesel在表和列上的查询泛型:尚未为 `<::Query as AsQuery>::Query` 实现trait `diesel::Table`

后端开发 2026-07-09

我正在尝试实现一个函数,使用diesel.rs从数据库中检索匹配的文本。

如果我事先知道表和列名,那样就能正常工作,但我需要查看几个表和列,因此我想创建一个通用函数,因为查询总是相同的。我尝试从一些回答中拼凑出思路,例如 将任意Diesel表作为参数传递https://github.com/diesel-rs/diesel/discussions/3887https://github.com/diesel-rs/diesel/discussions/2999,但我觉得自己还缺少某些东西。

作为一个最小的例子,我创建了两个相似的表:

CREATE TABLE IF NOT EXISTS my_one_table(
    id                BIGINT UNSIGNED NOT NULL,
    column1           VARCHAR(50)     NOT NULL,
    column2           VARCHAR(50)     NOT NULL,
    PRIMARY KEY(id)
    );
CREATE TABLE IF NOT EXISTS my_other_table(
    id                BIGINT UNSIGNED NOT NULL,
    column1           VARCHAR(50)     NOT NULL,
    column2           VARCHAR(50)     NOT NULL,
    PRIMARY KEY(id)
    );

并使用下面的Rust代码:

use diesel::prelude::*;
use diesel::dsl;
use diesel::query_dsl::methods::FilterDsl;
use diesel::query_dsl::LoadQuery;
use diesel::query_dsl::methods::LimitDsl;
use diesel::query_builder::AsQuery;

diesel::table! {
    my_one_table (id) {
        id -> Unsigned<Bigint>,
        column1 -> Varchar,
        column2 -> Varchar,
    }
}

diesel::table! {
    my_other_table (id) {
        id -> Unsigned<Bigint>,
        column1 -> Varchar,
        column2 -> Varchar,
    }
}


fn internal_get_by_id<T, C>(
    diesel_table: T, 
    column: C,     
    conn: &MysqlConnection,
    term: String,
) -> Vec<String>
where
    T: Table + FilterDsl<dsl::Like<C, String>>,
    C: Column + Expression<SqlType = diesel::sql_types::Text>,
    dsl::Filter<T, dsl::Like<C, String>>: LimitDsl,
    dsl::Limit<dsl::Filter<T, dsl::Like<C, String>>>: for<'a> LoadQuery<'a, MysqlConnection, String>, <T as AsQuery>::Query: diesel::Table
{
    diesel_table
        .filter(column.like(term))
        .select(column)
        .load(conn)
        .expect("Error") // TODO: Proper error handling outside the context of MWE
}

但我缺少一些trait bound,因为我收到了这样的错误信息:

   Compiling mwe v0.1.0 (/home/mox/projects/software/mwe)
error[E0277]: the trait bound `<<T as AsQuery>::Query as AsQuery>::Query: diesel::Table` is not satisfied
  --> src/db/completer_helpers.rs:25:1
   |
25 | / fn internal_get_by_id<T, C>(
26 | |     diesel_table: T,
27 | |     column: C,
28 | |     conn: &MysqlConnection,
...  |
34 | |     dsl::Filter<T, dsl::Like<C, String>>: LimitDsl,
35 | |     dsl::Limit<dsl::Filter<T, dsl::Like<C, String>>>: for<'a> LoadQuery<'a, MysqlConnection, String>, <T as AsQuery>::Query: diesel::Table
   | |__________________________________________________________________________________________________________________________________________^ the trait `diesel::Table` is not implemented for `<<T as AsQuery>::Query as AsQuery>::Query`
   |
   = note: required for `<<T as AsQuery>::Query as AsQuery>::Query` to implement `FilterDsl<expression::grouped::Grouped<expression::operators::Like<C, expression::bound::Bound<Text, std::string::String>>>>`
help: consider further restricting the associated type
   |
35 |     dsl::Limit<dsl::Filter<T, dsl::Like<C, String>>>: for<'a> LoadQuery<'a, MysqlConnection, String>, <T as AsQuery>::Query: diesel::Table, <<T as AsQuery>::Query as AsQuery>::Query: diesel::Table
   |                                                                                                                                           ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

error[E0275]: overflow evaluating the requirement `<<<T as AsQuery>::Query as AsQuery>::Query as FilterDsl<expression::grouped::Grouped<expression::operators::Like<C, expression::bound::Bound<..., ...>>>>>::Output == _`
  --> src/db/completer_helpers.rs:25:1
   |
25 | / fn internal_get_by_id<T, C>(
26 | |     diesel_table: T,
27 | |     column: C,
28 | |     conn: &MysqlConnection,
...  |
34 | |     dsl::Filter<T, dsl::Like<C, String>>: LimitDsl,
35 | |     dsl::Limit<dsl::Filter<T, dsl::Like<C, String>>>: for<'a> LoadQuery<'a, MysqlConnection, String>, <T as AsQuery>::Query: diesel::Table
   | |__________________________________________________________________________________________________________________________________________^

Some errors have detailed explanations: E0275, E0277.
For more information about an error, try `rustc --explain E0275`.
error: could not compile `mwe` (bin "mwe") due to 2 previous errors

到目前为止,编译器的错误信息在帮我找到正确的边界方面一直非常有用,但现在我遇到了困难:编译器建议在where子句中加入 <<T as AsQuery>::Query as AsQuery>::Query: diesel::Table,但如果我这样做,得到的基本还是相同的错误,只是被更多的 <> 包裹起来。

背景:为什么我要从我的表中只检索匹配的字符串?我正在尝试实现一个完成功能/建议机制,用于提示表中已存在的条目。

解决方案

花了一段时间,但我终于弄清楚我的代码应该长什么样子:

use diesel::prelude::*;

pub fn get_matching_strings<T, C>(
    conn: &mut MysqlConnection,
    table: T, 
    column: C,     
    term: String,
) -> Vec<String>
where
    T: diesel::Table + diesel::query_dsl::methods::FilterDsl<diesel::dsl::Like<C, String>>,
    diesel::dsl::Filter<T, diesel::dsl::Like<C, String>>: diesel::query_dsl::methods::SelectDsl<C>,
    diesel::dsl::Select<diesel::dsl::Filter<T, diesel::dsl::Like<C, String>>, C>: for<'a> diesel::query_dsl::LoadQuery<'a, MysqlConnection, String>,
    C: diesel::Expression + diesel::TextExpressionMethods + Copy,
    <C as diesel::Expression>::SqlType: diesel::sql_types::SqlType,
    std::string::String: diesel::expression::AsExpression<<C as Expression>::SqlType>,
{
    use diesel::query_dsl::methods::SelectDsl;
        // I used this for stepwise development of the bounds:
    //let filtered = table.filter(column.like(format!("%{term}%")));
    //let selected = filtered.select(column);
    //let loaded = selected.load(conn);
        //vec![]

    table
        .filter(column.like(format!("%{term}%")))
        .select(column)
        .load(conn)
        .unwrap_or_else(|err| vec![format!("Completion error: Could not load db data: {err:?}.")])

}

本质上我的边界条件是错的。再多看一些这篇很棒的回答之后,我终于理解,大多数情况下,我需要确保我要使用的方法在我的Table和 Column上可用。

我把查询分成几部分(并在此期间使用 vec![] 返回一个空数组)来按步骤地发展边界条件,如文中所述:

首先,如果没有trait bounds,且 vec![] 是函数体中唯一的语句,代码就能编译。

其次,我只是把 let filtered = ... 语句注释进来。编译器立刻会抛出一些并不特别有用的错误信息。但通过一些逻辑处理,就能理解:T必须是一个Table,且因为我们要对其调用 filter(),所以它必须实现 FilterDsl trait。既然我们用 like 作为谓词,diesel::dsl::Like 就成为 FilterDsl 的参数。此外,我们正在把列与字符串进行比较,因此这必须是 like 的参数。

第三步:这在编译时会产生一个错误,表示对 AsExpression 的绑定对 String 不成立。这很有道理,因为 like 要求第二个参数实现它。因此我可以直接复制编译器给出的那一行。

第四步:我们 like 的返回类型是 diesel::dsl::Filter<T, diesel::dsl::Like<C, String>>。在这个类型上我们将调用 select(),因此它必须具有相应的trait:diesel::query_dsl::methods::SelectDsl<C>(作为参数的C,因为我们只是选择那一列)。

按照这种方法,我最终得到了上面展示的代码。非常感谢 Neikos 给出的最初解释,帮助我走上正确的方向。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章