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

如何在Rust Diesel项目中传递任意表作为工具函数参数?

实现Diesel中通用的按ID查询工具函数

针对你的需求,我们可以通过关联类型+Diesel的trait约束来实现这个通用的按ID查询逻辑,让internal_get_by_id能适配任意带Uuid类型ID字段的表。下面是具体的实现步骤和代码:

第一步:明确依赖和基础类型

首先确保你已经导入了Diesel的核心模块,以及uuid相关依赖。假设你的项目使用PostgreSQL(如果是其他数据库,只需调整对应的Queryable约束中的数据库类型):

use diesel::prelude::*;
use uuid::Uuid;

// 假设你的自定义错误类型和连接包装器如下(根据实际项目调整)
#[derive(Debug)]
pub enum Error {
    DieselError(diesel::result::Error),
    // 其他错误类型...
}

impl From<diesel::result::Error> for Error {
    fn from(err: diesel::result::Error) -> Self {
        Error::DieselError(err)
    }
}

pub struct Conn {
    inner: PgConnection,
}

impl Conn {
    pub fn raw(&self) -> &PgConnection {
        &self.inner
    }
}

type Fallible<T> = Result<T, Error>;

第二步:修改Search trait,添加关联类型和约束

我们通过关联类型Table和IdColumn来绑定每个实体对应的表和ID字段,同时添加Diesel所需的trait约束,确保查询逻辑能正常编译:

pub trait Search: Sized + Queryable<<Self::Table as Table>::SqlType, Pg> {
    // 关联类型:当前实体对应的Diesel表
    type Table: Table + Query;
    // 关联类型:当前表的Uuid类型ID字段
    type IdColumn: Column + Expression<SqlType = Uuid> + Eq<Uuid>;

    fn internal_get_by_id(
        diesel_table: Self::Table,
        table_id: Self::IdColumn,
        conn: &Conn,
        id: Uuid,
    ) -> Fallible<Option<Self>> {
        diesel_table
            .filter(table_id.eq(id))
            .first(conn.raw())
            .optional()
            .map_err(Error::from)
    }

    // 每个实体需要实现的公开方法
    fn get_by_id(conn: &Conn, id: Uuid) -> Fallible<Option<Self>>;
}

约束说明:

  • Sized:确保我们可以返回Option<Self>(需要知道类型大小)
  • Queryable<...>:确保当前实体可以从对应表的查询结果中自动映射(Diesel的自动派生功能会帮我们实现这个trait)
  • Table + Query:确保关联的Table是Diesel可查询的表类型
  • Column + Expression<SqlType = Uuid> + Eq<Uuid>:确保ID字段是表的有效列,类型为Uuid,并且支持eq比较操作

第三步:为User和Item实现Search trait

假设你的User和Item结构体已经通过Diesel的#[derive(Queryable)]派生了对应的映射逻辑,现在只需实现Search trait并指定关联类型:

// 从schema.rs自动生成的结构体(通常Diesel会帮你生成,这里手动示例)
#[derive(Queryable)]
pub struct User {
    pub id: Uuid,
    pub name: String,
}

#[derive(Queryable)]
pub struct Item {
    pub id: Uuid,
    pub name: String,
}

impl Search for User {
    type Table = users::table;
    type IdColumn = users::id;

    fn get_by_id(conn: &Conn, id: Uuid) -> Fallible<Option<User>> {
        Self::internal_get_by_id(users::table, users::id, conn, id)
    }
}

impl Search for Item {
    type Table = items::table;
    type IdColumn = items::id;

    fn get_by_id(conn: &Conn, id: Uuid) -> Fallible<Option<Item>> {
        Self::internal_get_by_id(items::table, items::id, conn, id)
    }
}

替代方案:使用方法泛型(无需关联类型)

如果你不想用关联类型,也可以直接在internal_get_by_id方法上添加泛型约束,这样trait定义会更灵活,但约束会更冗长:

pub trait Search: Sized + QueryableByName {
    fn internal_get_by_id<T, I>(
        diesel_table: T,
        table_id: I,
        conn: &Conn,
        id: Uuid,
    ) -> Fallible<Option<Self>>
    where
        T: Table + Query,
        I: Column + Expression<SqlType = Uuid> + IntoSql<Uuid>,
        T::Query: FilterDsl<I::Eq<Uuid>>,
        <T::Query as FilterDsl<I::Eq<Uuid>>>::Output: LoadQuery<&'_ PgConnection, Self>,
    {
        diesel_table
            .filter(table_id.eq(id))
            .first(conn.raw())
            .optional()
            .map_err(Error::from)
    }

    fn get_by_id(conn: &Conn, id: Uuid) -> Fallible<Option<Self>>;
}

这种方式下,User和Item的实现和之前完全一致,不需要指定关联类型,但编译期的约束检查会更复杂。

总结

推荐使用关联类型的实现方式,代码结构更清晰,可读性更强,也更符合Diesel的设计习惯。这样你就可以轻松扩展到其他带Uuid类型ID的表,只需实现Search trait并指定对应的表和ID字段即可。

内容的提问来源于stack exchange,提问作者Pat

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.09 09:42:31