Rust泛型仓储实现中的递归错误E0275排查与解决
Rust中使用async_trait和diesel_async实现泛型仓储的递归错误E0275解决
问题概述
在Rust中基于async_trait和diesel_async实现通用插入仓储时,编译阶段触发递归错误E0275,该错误与Insertable trait的实现相关,根源是插入表达式的trait要求过载,导致编译器进行过度深层的trait评估。
当前代码通过diesel::insert_into(...).values(&item).returning(...)构建插入查询,触发了上述问题。编译器建议增加#![recursion_limit = "256"]仅能临时规避错误,将递归限制设为2048时会触发内部编译器错误(ICE),说明问题本质是插入查询的trait要求和关联类型过于复杂,无法通过调整递归限制解决,需要重构简化插入逻辑。
错误输出
PS D:\my-proyects\Vornix-azure\Ecomerce-Optica\backend\vornix-ecomerce-erp-service> cargo run warning: profiles for the non root package will be ignored, specify profiles at the workspace root: package: D:\my-proyects\Vornix-azure\Ecomerce-Optica\backend\vornix-billing-service\Cargo.toml workspace: D:\my-proyects\Vornix-azure\Ecomerce-Optica\backend\Cargo.toml warning: profiles for the non root package will be ignored, specify profiles at the workspace root: package: D:\my-proyects\Vornix-azure\Ecomerce-Optica\backend\vornix-ecomerce-erp-service\Cargo.toml workspace: D:\my-proyects\Vornix-azure\Ecomerce-Optica\backend\Cargo.toml warning: virtual workspace defaulting to `resolver = "1"` despite one or more workspace members being on edition 2021 which implies `resolver = "2"` note: to keep the current resolver, specify `workspace.resolver = "1"` in the workspace root's manifest note: to use the edition 2021 resolver, specify `workspace.resolver = "2"` in the workspace root's manifest note: for more details see https://doc.rust-lang.org/cargo/reference/resolver.html#resolver-versions Compiling vornix-core-rs v0.1.0 (D:\my-proyects\Vornix-azure\Ecomerce-Optica\backend\vornix-core-rs) error[E0275]: overflow evaluating the requirement `diesel::expression::operators::Eq<_, &_>: Insertable<_>` | = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`vornix_core_rs`) = note: required for `&diesel::expression::operators::Eq<_, _>` to implement `Insertable<_>` = note: 126 redundant requirements hidden = note: required for `&Option<Option<Option<Option<Option<Option<...>>>>>>` to implement `Insertable<_>` = note: the full name for the type has been written to 'D:\my-proyects\Vornix-azure\Ecomerce-Optica\backend\target\debug\deps\vornix_core_rs-da120415f0e5dafb.long-type-12242326637788567344.txt' = note: consider using `--verbose` to print the full type name to the console For more information about this error, try `rustc --explain E0275`. error: could not compile `vornix-core-rs` (lib) due to 1 previous error
当前实现代码
use async_trait::async_trait; use diesel::{ pg::Pg, query_builder::{AsQuery, InsertStatement, QueryFragment}, Identifiable, Insertable, Queryable, Selectable, Table, sql_types::SingleValue, QuerySource }; use diesel_async::{AsyncConnection, RunQueryDsl, methods::LoadQuery}; // 用于避免递归重构的主trait #[async_trait] pub trait BaseRepository: Send + 'static { // 明确定义关联类型 type Entity: Identifiable + Send + 'static; type Table: Table + QuerySource + Send + 'static; type Conn: AsyncConnection<Backend = Pg> + Send + 'static; type Schema; // 获取表的方法 fn table() -> Self::Table; // 重构后的插入方法 async fn insert<I>( &self, conn: &mut Self::Conn, item: I, ) -> Result<Self::Entity, diesel::result::Error> where Self::Entity: Queryable<Self::Schema, Pg> + Selectable<Pg> + 'static, Self::Schema: SingleValue + Send + 'static, I: Insertable<Self::Table> + Send + 'static, I::Values: QueryFragment<Pg> + Send + 'static, InsertStatement<Self::Table, I::Values>: AsQuery + Send + 'static, <InsertStatement<Self::Table, I::Values> as AsQuery>::Query: LoadQuery<'static, Self::Conn, Self::Entity> + Send, <Self::Table as QuerySource>::FromClause: Send, { let query = diesel::insert_into(Self::table()) .values(&item) .returning(Self::table().all_columns()); query.get_result(conn).await } }
仓储实现示例
#[async_trait] impl BaseRepository for UserRepository { type Entity = NewUser; type Table = users::table; type Conn = diesel_async::pooled_connection::AsyncDieselConnection<Pg>; type Schema = (diesel::sql_types::Integer, diesel::sql_types::Text); fn table() -> Self::Table { users::table } }
递归限制设为2048时的ICE错误
741: 0x7ff82e25b71f - <rustc_trait_selection[696cc11a6b5294cd]::traits::select::SelectionContext>::poly_select 742: 0x7ff82e1084ef - core[b826f7616f2dc1fa]::slice::sort::unstable::heapsort::heapsort::<rustc_span[3904f55a5cfd74f4]::def_id::DefId, <[rustc_span[3904f55a5cfd74f4]::def_id::DefId]>::sort_unstable_by_key<(i64, usize), rustc_trait_selection[696cc11a6b5294cd]::traits::specialize::specialization_graph_provider::{closure#0}>::{closure#0}> 743: 0x7ff82e868d21 - <rustc_data_structures[5dc475d9950331dc]::profiling::_::InternalBitFlags as core[b826f7616f2dc1fa]::str::traits::FromStr>::from_str 744: 0x7ff8b137fef0 - SetSecurityDescriptorControl 745: 0x7ff8b409bf29 - RtlUserFiberStart error: the compiler unexpectedly panicked. this is a bug.
重构与简化建议
1. 修正Schema关联类型的约束冲突
原代码中Self::Schema: SingleValue的约束与示例中Schema为元组的定义矛盾(SingleValue要求单个SQL类型),这会导致编译器在推导Queryable时产生不必要的递归。应移除SingleValue约束,或让Schema匹配实体的完整SQL类型元组:
// 修改trait中的where约束,移除SingleValue Self::Entity: Queryable<Self::Schema, Pg> + Selectable<Pg> + 'static, Self::Schema: Send + 'static,
2. 简化Insert方法的泛型约束
避免在单个方法中堆积过多关联类型约束,将部分约束转移到实体或仓储的关联类型中,或减少不必要的推导:
async fn insert<I>( &self, conn: &mut Self::Conn, item: I, ) -> Result<Self::Entity, diesel::result::Error> where I: Insertable<Self::Table> + Send + 'static, // 仅保留必要的执行约束,让编译器自动推导其他关联类型 InsertStatement<Self::Table, I::Values>: LoadQuery<'_, Self::Conn, Self::Entity>, { diesel::insert_into(Self::table()) .values(item) // 直接传item,避免&item导致的额外Insertable推导 .returning(<Self::Entity as Selectable<Pg>>::selection()) // 使用Selectable的默认选择器 .get_result(conn) .await }
3. 分离查询构建与执行逻辑
将查询构建抽为单独的非async函数,降低单个方法内的trait推导复杂度:
#[async_trait] pub trait BaseRepository: Send + 'static { // ... 其他关联类型和方法 ... fn build_insert_query<I>(item: I) -> InsertStatement<Self::Table, I::Values> where I: Insertable<Self::Table>, { diesel::insert_into(Self::table()).values(item) } async fn insert<I>( &self, conn: &mut Self::Conn, item: I, ) -> Result<Self::Entity, diesel::result::Error> where I: Insertable<Self::Table> + Send + 'static, InsertStatement<Self::Table, I::Values>: LoadQuery<'_, Self::Conn, Self::Entity>, { Self::build_insert_query(item) .returning(<Self::Entity as Selectable<Pg>>::selection()) .get_result(conn) .await } }
4. 明确实体的Insertable与Selectable实现
确保NewUser等实体正确实现所需trait,避免编译器进行模糊推导:
// 确保NewUser实现Insertable,指定对应的表 #[derive(Insertable)] #[diesel(table_name = users)] pub struct NewUser { // ... 字段定义 ... } // 确保实体实现Selectable,让returning能正确推导返回列 #[derive(Selectable, Queryable)] #[diesel(table_name = users)] pub struct User { // ... 字段定义 ... }
5. 避免过度泛型化
如果业务场景允许,可以为Insert方法指定更具体的类型(比如直接使用Self::Entity作为插入类型),减少泛型推导的复杂度:
async fn insert( &self, conn: &mut Self::Conn, item: Self::Entity, ) -> Result<Self::Entity, diesel::result::Error> where Self::Entity: Insertable<Self::Table> + Queryable<Self::Schema, Pg> + Selectable<Pg> + 'static, InsertStatement<Self::Table, <Self::Entity as Insertable<Self::Table>>::Values>: LoadQuery<'_, Self::Conn, Self::Entity>, { diesel::insert_into(Self::table()) .values(item) .returning(<Self::Entity as Selectable<Pg>>::selection()) .get_result(conn) .await }
内容的提问来源于stack exchange,提问作者vornix
相关产品推荐
相关产品推荐

