gpt4 book ai didi

generics - 如何通过抽象将使用 Diesel 的多个功能合并为一个?

转载 作者:行者123 更新时间:2023-11-29 08:29:47 38 4
gpt4 key购买 nike

我有以下两个功能:

pub fn get_most_recent_eth_entry(conn: &SqliteConnection) -> Result<i32, Error> {
let res = types::ethereum::table
.order(types::ethereum::time.desc())
.limit(1)
.load::<types::ETHRecord>(&*conn);
match res {
Ok(x) => {
if x.len() > 0 {
Ok(x.get(0).unwrap().time)
} else {
Ok(0)
}
}
Err(err) => Err(format_err!("Error here! {:?}", err)),
}
}

pub fn get_most_recent_btc_entry(conn: &SqliteConnection) -> Result<i32, Error> {
let res = types::bitcoin::table
.order(types::bitcoin::time.desc())
.limit(1)
.load::<types::BTCRecord>(&*conn);
match res {
Ok(x) => {
if x.len() > 0 {
Ok(x.get(0).unwrap().time)
} else {
Ok(0)
}
}
Err(err) => Err(format_err!("Error here! {:?}", err)),
}
}

我想将两者合并为一个功能。我尝试了几种不同的方法,但是

  1. 我对 Rust 很陌生
  2. Diesel 有一些奇怪的类型(或者至少给人的感觉是这样)

有哪些方法可以将这两个函数(它们仅在字段 types::ethereumETHRecord 上不同)合并到一个统一函数 get_most_recent_entry?

这些是我的数据库结构定义(等效地定义了 SQL 模式):

#[derive(Insertable, Queryable, Debug)]
#[table_name="bitcoin"]
pub struct BTCRecord {
pub time: i32,
pub market_cap: f32,
pub price_btc: f32,
pub price_usd: f32,
pub vol_usd: f32,
}

的类型
`types::ethereum::time` is `database::types::__diesel_infer_schema::infer_bitcoin::bitcoin::columns::time`

的类型
`types::ethereum::table` is
`database::types::__diesel_infer_schema::infer_bitcoin::bitcoin::table`

最佳答案

首先,让我们从 MCVE 开始.这是专业程序员在试图理解问题时使用的工具。它删除了无关的细节,但为任何人 提供了足够的细节,以便能够拾取它并重现情况。比较此处有多少您未提供的代码。每一个缺失的部分都是回答者必须猜测的,以及你的时间和他们产生的时间。

[dependencies]
diesel = { version = "1.0.0-beta", features = ["sqlite"] }
#[macro_use]
extern crate diesel;

use diesel::prelude::*;
use diesel::SqliteConnection;

mod types {
table! {
bitcoin (time) {
time -> Int4,
}
}

table! {
ethereum (time) {
time -> Int4,
}
}

#[derive(Insertable, Queryable, Debug)]
#[table_name="bitcoin"]
pub struct BtcRecord {
pub time: i32,
}

#[derive(Insertable, Queryable, Debug)]
#[table_name="ethereum"]
pub struct EthRecord {
pub time: i32,
}
}

pub fn get_most_recent_eth_entry(conn: &SqliteConnection) -> Result<i32, String> {
let res = types::ethereum::table
.order(types::ethereum::time.desc())
.limit(1)
.load::<types::EthRecord>(&*conn);
match res {
Ok(x) => {
if x.len() > 0 {
Ok(x.get(0).unwrap().time)
} else {
Ok(0)
}
}
Err(err) => Err(format!("Error here! {:?}", err)),
}
}

pub fn get_most_recent_btc_entry(conn: &SqliteConnection) -> Result<i32, String> {
let res = types::bitcoin::table
.order(types::bitcoin::time.desc())
.limit(1)
.load::<types::BtcRecord>(&*conn);
match res {
Ok(x) => {
if x.len() > 0 {
Ok(x.get(0).unwrap().time)
} else {
Ok(0)
}
}
Err(err) => Err(format!("Error here! {:?}", err)),
}
}

接下来,在两段代码之间执行差异以识别差异。你说:

which differ in only the fields types::ethereum and ETHRecord

但是,它们在四个位置上有所不同。仅仅因为某些东西具有相同的前缀并不意味着您可以传递该前缀。模块不是 Rust 运行时存在的概念:

pub fn get_most_recent_eth_entry(conn: &SqliteConnection) -> Result<i32, String> {
// ^^^^^^^^^^^^^^^^^^^^^^^^^
let res = types::ethereum::table
// ^^^^^^^^
.order(types::ethereum::time.desc())
// ^^^^^^^^
.limit(1)
.load::<types::EthRecord>(&*conn);
// ^^^^^^^^^

让我们复制并粘贴其中一个函数,并用虚拟变量替换所有唯一值:

pub fn get_most_recent_entry<'a, Tbl, Expr, Record>(
conn: &SqliteConnection,
table: Tbl,
time: Expr,
) -> Result<i32, String> {
let res = table
.order(time.desc())
.limit(1)
.load::<Record>(&*conn);
// ...

下一部分并不漂亮。基本上,编译器会一一告诉您每个未满足的特征界限。您“只需”将每个错误复制回代码以设置所有约束:

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>>,
<Tbl as OrderDsl<Desc<Expr>>>::Output: LimitDsl,
<<Tbl as OrderDsl<Desc<Expr>>>::Output as LimitDsl>::Output: RunQueryDsl<SqliteConnection> + Query,
Sqlite: HasSqlType<<<<Tbl as OrderDsl<Desc<Expr>>>::Output as LimitDsl>::Output as Query>::SqlType>,
<<Tbl as OrderDsl<Desc<Expr>>>::Output as LimitDsl>::Output: QueryFragment<Sqlite>,
<<Tbl as OrderDsl<Desc<Expr>>>::Output as LimitDsl>::Output: QueryId,
Record: Queryable<<<<Tbl as OrderDsl<Desc<Expr>>>::Output as LimitDsl>::Output as Query>::SqlType, Sqlite>,

这会导致新的错误:

error[E0609]: no field `time` on type `&Record`
--> src/main.rs:64:38
|
64 | Ok(x.get(0).unwrap().time)
| ^^^^

您不能在泛型类型上假设任何字段,我们需要一个特征:

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

你:

  • 为两种具体类型实现特征
  • 将此特征绑定(bind)到 Record
  • 在方法中调用.time()

一起:

#[macro_use]
extern crate diesel;

use diesel::prelude::*;
use diesel::SqliteConnection;

mod types {
table! {
bitcoin (time) {
time -> Int4,
}
}

table! {
ethereum (time) {
time -> Int4,
}
}

#[derive(Insertable, Queryable, Debug)]
#[table_name = "bitcoin"]
pub struct BtcRecord {
pub time: i32,
}

#[derive(Insertable, Queryable, Debug)]
#[table_name = "ethereum"]
pub struct EthRecord {
pub time: i32,
}
}

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

impl Time for types::EthRecord {
fn time(&self) -> i32 {
self.time
}
}

impl Time for types::BtcRecord {
fn time(&self) -> i32 {
self.time
}
}

use diesel::sqlite::Sqlite;
use diesel::types::HasSqlType;
use diesel::query_dsl::methods::{LimitDsl, OrderDsl};
use diesel::expression::operators::Desc;
use diesel::query_builder::{Query, QueryFragment, QueryId};
use diesel::Queryable;

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>>,
<Tbl as OrderDsl<Desc<Expr>>>::Output: LimitDsl,
<<Tbl as OrderDsl<Desc<Expr>>>::Output as LimitDsl>::Output: RunQueryDsl<SqliteConnection> + Query,
Sqlite: HasSqlType<<<<Tbl as OrderDsl<Desc<Expr>>>::Output as LimitDsl>::Output as Query>::SqlType>,
<<Tbl as OrderDsl<Desc<Expr>>>::Output as LimitDsl>::Output: QueryFragment<Sqlite>,
<<Tbl as OrderDsl<Desc<Expr>>>::Output as LimitDsl>::Output: QueryId,
Record: Queryable<<<<Tbl as OrderDsl<Desc<Expr>>>::Output as LimitDsl>::Output as Query>::SqlType, Sqlite> + Time,
{
let res = table.order(time.desc()).limit(1).load::<Record>(&*conn);
match res {
Ok(x) => {
if x.len() > 0 {
Ok(x.get(0).unwrap().time())
} else {
Ok(0)
}
}
Err(err) => Err(format!("Error here! {:?}", err)),
}
}

pub fn get_most_recent_eth_entry(conn: &SqliteConnection) -> Result<i32, String> {
get_most_recent_entry::<_, _, types::EthRecord>(
conn,
types::ethereum::table,
types::ethereum::time,
)
}

pub fn get_most_recent_btc_entry(conn: &SqliteConnection) -> Result<i32, String> {
get_most_recent_entry::<_, _, types::BtcRecord>(
conn,
types::bitcoin::table,
types::bitcoin::time,
)
}

接下来的步骤需要更深入地研究 Diesel。 helper_types module包含允许我们缩短边界的类型别名:

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>>: LimitDsl,
Limit<Order<Tbl, Desc<Expr>>>: RunQueryDsl<SqliteConnection>
+ Query
+ QueryFragment<Sqlite>
+ QueryId,
Sqlite: HasSqlType<<Limit<Order<Tbl, Desc<Expr>>> as Query>::SqlType>,
Record: Queryable<<Limit<Order<Tbl, Desc<Expr>>> as Query>::SqlType, Sqlite> + Time,

还有一个包含所有Query*相关子特征的特征: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>>: LimitDsl,
Limit<Order<Tbl, Desc<Expr>>>: LoadQuery<SqliteConnection, Record>,
Record: Time,

然后您可以使用 Diesel 的 first 函数和 Result 的组合器来缩短整个函数:

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))
}

关于generics - 如何通过抽象将使用 Diesel 的多个功能合并为一个?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47874398/

38 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com