如何访问在 Diesel 中通常访问的 table 的具体列?

How to access concrete columns of a table accessed generically in Diesel?

我很难为这个问题选择一个准确简洁的标题。

这个问题扩展了@Shepmaster 在这里给出的出色答案:

其解法为:

use diesel::expression::operators::Desc;
use diesel::helper_types::{Limit, Order};
use diesel::query_dsl::methods::{LimitDsl, OrderDsl};
use diesel::query_dsl::LoadQuery;

pub fn get_most_recent_entry<'a, Tbl, Expr, Record>(
    conn: &SqliteConnection,
    table: Tbl,
    time: Expr,
) -> Result<i32, String>
where
    Expr: diesel::ExpressionMethods,
    Tbl: OrderDsl<Desc<Expr>>,
    Order<Tbl, Desc<Expr>>: LoadQuery<SqliteConnection, Record> + LimitDsl,
    Limit<Order<Tbl, Desc<Expr>>>: LoadQuery<SqliteConnection, Record>,
    Record: Time,
{
    table
        .order(time.desc())
        .first(conn)
        .optional()
        .map(|x| x.map_or(0, |x| x.time()))
        .map_err(|e| format!("Error here! {:?}", e))
}

在上面的代码片段中,TableExpression 作为参数提供。如何进一步抽象此函数,使得 TableExpression 都不必作为参数传递?

我已经想出了如何抽象 Table(见下文),但我不知道如何删除 Expression 参数以便该函数可以与任何Table 包含 time 列。

use diesel::expression::operators::Desc;
use diesel::helper_types::{Limit, Order};
use diesel::query_dsl::methods::{LimitDsl, OrderDsl};
use diesel::query_dsl::LoadQuery;
use diesel::SqliteConnection;
use crate::diesel::{RunQueryDsl, OptionalExtension};
use diesel::associations::HasTable;

pub trait Time {
    fn time(&self) -> i32;
}

pub fn get_most_recent_entry<'a, Tbl, Expr, Record>(
    conn: &SqliteConnection,
    time: Expr,
) -> Result<i32, String>
where
    Expr: diesel::ExpressionMethods,
    Tbl: HasTable,
    Tbl::Table: OrderDsl<Desc<Expr>>,
    Order<Tbl::Table, Desc<Expr>>: LoadQuery<SqliteConnection, Record> + LimitDsl,
    Limit<Order<Tbl::Table, Desc<Expr>>>: LoadQuery<SqliteConnection, Record>,
    Record: Time,
{
    Tbl::table()
        .order(time.desc())
        .first(conn)
        .optional()
        .map(|x| x.map_or(0, |x| x.time()))
        .map_err(|e| format!("Error here! {:?}", e))
}

已更新

像这样:

# Cargo.toml
[dependencies]
diesel = { version = "1.4.5", features = ["sqlite", "extras"] }
#[derive(Queryable)]
pub struct Cat {
    id: u32,
    name: String,
    time: i32 //time record created
}

impl Time for Cat {
    fn time(&self) -> i32 {
        self.time
    }
}

pub fn get_most_recent_entry<'a, Tbl, Record>(
    conn: &SqliteConnection
) -> Result<i32, String>
where 
    // Don't know how to setup trait bounds for the expression
    // to express any table that has the same time column
{
    Tbl::table()
        .order(Tbl::columns::time.desc()) // Something like this
        .first(conn)
        .optional()
        .map(|x| x.map_or(0, |x| x.time()))
        .map_err(|e| format!("Error here! {:?}", e))
}

use crate::schema::cat;
fn main() {
    let conn = pool.get()?;
    get_most_recent_entry::<cat::dsl::cat, _>(&conn)
}



不可能以这种通用方式访问列。每列都是它自己的零大小结构,放置在以 table 命名的模块中。 (有关 table! 生成的代码的详细信息,请参阅 "Schema in depth" guide)。 Rust 不提供任何选项来通过泛型处理来自同一模块的不同类型。

写的可以只提供你自己的特征,允许你指定相应的列并为相应的table(或模型,或任何你想要的类型)实现它:

trait MyTimeColumnHelper {
    type TimeColumn: Default;
}


impl MyTimeColumnHelper for crate::schema::cat::table {
     type TimeColumn = crate::schema::cat::time;
}

这样您的泛型函数就可以绑定到这个附加特征:

pub fn get_most_recent_entry<'a, Tbl, Record>(
    conn: &SqliteConnection
) -> Result<i32, String>
where 
    MyTimeColumnHelper::TimeColumn: diesel::ExpressionMethods,
    Tbl: HasTable + MyTimeColumnHelper,
    Tbl::Table: OrderDsl<Desc<MyTimeColumnHelper::TimeColumn>>,
    Order<Tbl::Table, Desc<MyTimeColumnHelper::TimeColumn>>: LoadQuery<SqliteConnection, Record> + LimitDsl,
    Limit<Order<Tbl::Table, Desc<MyTimeColumnHelper::TimeColumn>>>: LoadQuery<SqliteConnection, Record>,
    Record: Time,
    Tbl::Table: OrderDsl<Desc<MyTimeColumnHelper::TimeColumn>>,
    Order<Tbl::Table, Desc<MyTimeColumnHelper::TimeColumn>>: LoadQuery<SqliteConnection, Record> + LimitDsl,
    Limit<Order<Tbl::Table, Desc<MyTimeColumnHelper::TimeColumn>>>: LoadQuery<SqliteConnection, Record>,
    Record: Time,
{
    Tbl::table()
        .order(MyTimeColumnHelper::TimeColumn::default().desc()) 
        .first(conn)
        .optional()
        .map(|x| x.map_or(0, |x| x.time()))
        .map_err(|e| format!("Error here! {:?}", e))
}