You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

混用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 11:22:31