Diesel查询动态排序:DynOrderDsl trait编译溢出错误排查
解决Diesel动态排序链式查询的E0275递归溢出错误
你遇到的E0275错误是因为编译器在检查DynOrderDsl的trait边界时陷入了递归求值循环——虽然你复制了order_query的边界,但链式调用的trait上下文和单独调用方法的上下文不一样,BoxedDsl的生命周期和关联类型形成了循环依赖,导致编译器无限递归检查。
修正思路
核心是打破递归检查的循环:给trait添加显式生命周期,明确关联类型的约束,避免让编译器隐式推导可能形成循环的边界。
修正后的代码示例
先假设你原有的QueryOrdering和order_query是这样的:
use diesel::query_builder::BoxedDsl; use diesel::sql_types::Expr; // 假设你的排序枚举定义 enum QueryOrdering { TitleAsc, TitleDesc, ScoreAsc, ScoreDesc, } impl QueryOrdering { // 你说能正常运行的order_query方法 pub fn order_query<'a, Q>(query: Q, ordering: Self) -> Q::Output where Q: BoxedDsl<'a, Expr>, Q::Output: BoxedDsl<'a, Expr>, { // 原有的排序逻辑,比如根据枚举选择排序字段和方向 match ordering { QueryOrdering::TitleAsc => query.order_by(posts::title.asc()), QueryOrdering::TitleDesc => query.order_by(posts::title.desc()), QueryOrdering::ScoreAsc => query.order_by(posts::score.asc()), QueryOrdering::ScoreDesc => query.order_by(posts::score.desc()), } } }
接下来是修正后的DynOrderDsl:
// 给trait添加显式生命周期'a,明确关联类型的约束 trait DynOrderDsl<'a> { type Output: BoxedDsl<'a, Expr>; fn order_by_dyn(self, ordering: QueryOrdering) -> Self::Output; } // 实现trait时,把生命周期和关联类型的约束写死,避免递归推导 impl<'a, Q> DynOrderDsl<'a> for Q where Q: BoxedDsl<'a, Expr>, Q::Output: BoxedDsl<'a, Expr>, { type Output = Q::Output; fn order_by_dyn(self, ordering: QueryOrdering) -> Self::Output { QueryOrdering::order_query(self, ordering) } } // 现在链式调用的example函数可以正常编译了 fn example<'a>(query: impl BoxedDsl<'a, Expr>) -> impl BoxedDsl<'a, Expr> { query .order_by_dyn(QueryOrdering::ScoreDesc) .order_by_dyn(QueryOrdering::TitleAsc) }
额外优化:用具体的BoxedQuery类型
如果还是有推导问题,直接用BoxedQuery代替泛型trait,减少编译器的推导负担:
use diesel::pg::Pg; use diesel::query_builder::BoxedQuery; fn example<'a>(query: BoxedQuery<'a, Pg>) -> BoxedQuery<'a, Pg> { query .order_by_dyn(QueryOrdering::ScoreDesc) .order_by_dyn(QueryOrdering::TitleAsc) }
为什么这么改能解决问题
原来的trait定义没有显式生命周期,编译器会隐式推导BoxedDsl的生命周期,而Self: BoxedDsl<'_, Expr>和Self::Output: BoxedDsl<'_, Expr>的组合会让编译器陷入“检查Self是否满足,需要检查Self::Output,而检查Self::Output又需要检查它的Output,无限循环”的困境。添加显式生命周期和明确的关联类型后,编译器能直接确定每个步骤的类型边界,不会再递归溢出。
内容的提问来源于stack exchange,提问作者susitsm
相关产品推荐
相关产品推荐

