混用embed与belongs_to时缺失CompatibleType trait约束问题
Diesel v2.1.0中
#[diesel(embed)]与belongs_to关联共存时的编译错误解决 问题描述
使用Diesel v2.1.0读取MySQL数据库时,尝试加载包含枚举字段的结构体:
- 枚举包含单元变体和单值元组变体,通过
#[diesel(embed)]将表中的两列(variant和value)映射为结构体的单个枚举字段 - 同时为结构体定义
belongs_to关联后,编译器报错未实现load_dsl::private::CompatibleType<…>trait
最小复现代码
use diesel::deserialize; use diesel::mysql::Mysql; use diesel::prelude::*; use diesel::sql_types::{Integer, Nullable}; diesel::table! { parents { id -> Integer, } } diesel::table! { children { id -> Integer, parent_id -> Integer, // (variant, value) to be coerced into a single field variant -> Integer, value -> Nullable<Integer>, } } #[derive(Debug, Identifiable, Queryable, Selectable)] #[diesel(table_name = parents)] #[diesel(check_for_backend(Mysql))] pub struct Parent { id: i32, } #[derive(Debug)] pub enum CustomEnum { VariantWithValue(i32), AnotherVariant, YetAnotherVariant, } impl Selectable<Mysql> for CustomEnum { type SelectExpression = (children::variant, children::value); fn construct_selection() -> Self::SelectExpression { (children::variant, children::value) } } impl Queryable<(Integer, Nullable<Integer>), Mysql> for CustomEnum { type Row = (i32, Option<i32>); fn build(row: Self::Row) -> deserialize::Result<Self> { use CustomEnum::*; let (variant, value) = row; match (variant, value) { (1, Some(n)) => Ok(VariantWithValue(n)), (2, _) => Ok(AnotherVariant), (3, _) => Ok(YetAnotherVariant), _ => Err("invalid variant".into()), } } } #[derive(Debug, Associations, Identifiable, Queryable, Selectable)] #[diesel( table_name = children, belongs_to(Parent, foreign_key = parent_id) )] #[diesel(check_for_backend(Mysql))] pub struct Child { id: i32, parent_id: i32, #[diesel(embed)] custom: CustomEnum, } fn load_children_per_parent(connection: &mut MysqlConnection) -> Vec<(Parent, Vec<Child>)> { let parents = parents::table.load::<Parent>(connection).unwrap(); let children = Child::belonging_to(&parents) .load::<Child>(connection) .unwrap() .grouped_by(&parents); parents.into_iter().zip(children).collect() }
编译错误信息
$ cargo build Compiling diesel-compatible-type v0.1.0 (/tmp/diesel-compatible-type) error[E0277]: the trait bound `(diesel::sql_types::Integer, diesel::sql_types::Integer, diesel::sql_types::Integer, diesel::sql_types::Nullable<diesel::sql_types::Integer>): load_dsl::private::CompatibleType<Child, Mysql>` is not satisfied --> src/lib.rs:76:24 | 76 | .load::<Child>(connection) | ---- ^^^^^^^^^^ the trait `load_dsl::private::CompatibleType<Child, Mysql>` is not implemented for `(diesel::sql_types::Integer, diesel::sql_types::Integer, diesel::sql_types::Integer, diesel::sql_types::Nullable<diesel::sql_types::Integer>)` | | | required by a bound introduced by this call | = help: the following other types implement trait `load_dsl::private::CompatibleType<U, DB>`: (ST0, ST1) (ST0, ST1, ST2) (ST0, ST1, ST2, ST3) (ST0, ST1, ST2, ST3, ST4) (ST0, ST1, ST2, ST3, ST4, ST5) (ST0, ST1, ST2, ST3, ST4, ST5, ST6) (ST0, ST1, ST2, ST3, ST4, ST5, ST6, ST7) (ST0, ST1, ST2, ST3, ST4, ST5, ST6, ST7, ST8) and 24 others = note: required for `SelectStatement<FromClause<table>, DefaultSelectClause<FromClause<table>>, NoDistinctClause, WhereClause<Grouped<In<parent_id, Many<Integer, &i32>>>>>` to implement `LoadQuery<'_, _, Child>` = note: the full type name has been written to '/tmp/diesel-compatible-type/target/debug/deps/diesel_compatible_type-04c41dd73697d996.long-type-17130066208825338060.txt' note: required by a bound in `diesel::RunQueryDsl::load` --> /<redacted>/.cargo/registry/src/index.crates.io-6f17d22bba15001f/diesel-2.1.0/src/query_dsl/mod.rs:1543:15 | 1543 | Self: LoadQuery<'query, Conn, U>, | ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `diesel::RunQueryDsl::load` For more information about this error, try `rustc --explain E0277`. error: could not compile `diesel-compatible-type` (lib) due to previous error
解决方案
错误的核心原因是:Child::belonging_to(&parents)生成的默认查询会直接选择children表的所有列(id, parent_id, variant, value),但Child结构体通过#[diesel(embed)]将variant和value合并为custom: CustomEnum,导致默认查询的返回列类型(四元组)与Child的Queryable实现不匹配。
只需在查询中显式调用select(Child::as_select()),让Diesel使用Selectable派生的逻辑生成正确的选择表达式(对应Child结构体的字段,包括嵌入的枚举对应的两列):
修改后的load_children_per_parent函数:
fn load_children_per_parent(connection: &mut MysqlConnection) -> Vec<(Parent, Vec<Child>)> { let parents = parents::table.load::<Parent>(connection).unwrap(); let children = Child::belonging_to(&parents) .select(Child::as_select()) // 显式指定选择表达式 .load::<Child>(connection) .unwrap() .grouped_by(&parents); parents.into_iter().zip(children).collect() }
这样Diesel就能正确将查询结果映射到Child结构体,解决编译错误。
内容的提问来源于stack exchange,提问作者bleuse
相关产品推荐
相关产品推荐

