如何正确将Rust Diesel查询移至独立函数?
问题
希望将包含多表连接的大型共享查询代码移至独立函数,同时保留其惰性查询特性,以便后续在不同位置通过filter、limit等方法执行。但指定具体返回类型时,类型过于庞大且难以阅读。
尝试将返回类型设置为impl QueryDsl和Box<impl QueryDsl>:
fn users_query() -> impl QueryDsl { users_dsl .inner_join(...) .inner_join(...) .inner_join(...) .left_join(...) .left_join(...) .left_join(...) .left_join(...) }
代码可编译,但调用filter或limit时出现错误:
error[E0275]: overflow evaluating the requirement `_: Sized` --> users/src/repository.rs:278:14 | 278 | .filter(users_table::id.eq(user_id.as_sql::<sql_types::Uuid>())) | ^^^^^^ | = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`site_users_view`) = note: required for `impl QueryDsl` to implement `FilterDsl<_>` error[E0277]: the trait bound `impl QueryDsl: Table` is not satisfied --> users/src/repository.rs:288:43 | 288 | let query = users_query().limit(limit.into()); | ^^^^^ the trait `Table` is not implemented for `impl QueryDsl` | = help: the following other types implement trait `Table`: Only<S> diesel_migrations::migration_harness::__diesel_schema_migrations::table pg::metadata_lookup::pg_namespace::table pg::metadata_lookup::pg_type::table schema::permissions::table schema::users::table addresses::table cities::table and 7 others = note: required for `impl QueryDsl` to implement `LimitDsl`
请问如何正确将查询移至独立函数?
解决方案
问题核心在于QueryDsl是基础 trait,未包含FilterDsl、LimitDsl等扩展方法的约束,且LimitDsl要求实现Table trait——而多表连接查询本身并不满足该条件。以下是两种可行的解决方式:
方式一:精准指定返回类型的 trait 约束
不要仅返回impl QueryDsl,而是结合后续要用到的所有扩展 trait,同时关联查询的输出类型:
use diesel::{QueryDsl, FilterDsl, LimitDsl, Selectable, Queryable}; use schema::users::dsl::*; use uuid::Uuid; // 定义与查询结果匹配的结构体 #[derive(Selectable, Queryable, Debug)] struct UserWithRelations { id: Uuid, name: String, // 补充关联表对应的字段 } fn users_query() -> impl QueryDsl + FilterDsl<users::id::Eq<Uuid>> + LimitDsl + Selectable<SelectionTarget = UserWithRelations> { users .inner_join(...) .inner_join(...) .left_join(...) // 确保select语句与结构体字段对应 .select(UserWithRelations::as_select()) }
这种方式保留了查询的静态类型特性,编译时就能检查错误,同时避免写出冗长的具体类型。
方式二:使用BoxedQuery封装复杂查询
如果 trait 约束过于繁琐,或者需要动态调整查询逻辑,用BoxedQuery可以隐藏具体类型,同时保留惰性执行能力:
use diesel::query_dsl::methods::BoxedDsl; use diesel::pg::Pg; // 对应你的数据库后端,比如MySQL用`diesel::mysql::Mysql` fn users_query() -> BoxedQuery<'static, Pg> { users .inner_join(...) .inner_join(...) .left_join(...) .select(...) // 这里可以是具体字段或结构体的as_select() .into_boxed() // 将查询装箱为BoxedQuery }
BoxedQuery自动实现了FilterDsl、LimitDsl等常用 trait,后续调用filter、limit等方法不会报错。'static生命周期适用于无引用参数的查询,若查询包含动态引用,需调整为对应生命周期(比如'a)。
额外注意
确保导入了Diesel的必要 trait,推荐直接导入diesel::prelude::*,避免因trait未导入导致的编译错误。
内容的提问来源于stack exchange,提问作者eirenik0
相关产品推荐
相关产品推荐

