如何将使用Diesel的多个功能合并为一个抽象? [英] How do I combine multiple functions using Diesel into one through abstraction?
问题描述
我有以下两个函数: 我想将两者合并为一个函数。我尝试了几种不同的方式,但是 有什么方法可以合并这两个函数字段 和 和 让我们从 MCVE 开始。这是专业程序员在试图理解问题时使用的工具。它去除了多余的细节,但为任何人提供了足够的细节,以便能够拾取并重现这种情况。比较您没有提供的代码。每个缺失的部分是回答者必须猜测的东西,以及您的时间和生成时间。 接下来,在两段代码之间执行差异以确定差异。你说过: 只有字段 然而,它们在四个地方有所不同。仅仅因为某些东西具有相同的前缀并不意味着你可以传递前缀。模块不是运行时存在于Rust中的概念: 让我们复制并粘贴其中一个函数并将所有唯一值替换为傻瓜: 接下来的部分并不漂亮。基本上,编译器会告诉你一个接一个没有被满足的特性。您只需将每个错误复制回代码以设置所有约束: 这会导致新的错误: 你不能假设泛型的任何字段,我们需要一个特征: 您: 接下来的步骤需要深入探索Diesel。 还有一个特性,它包含了所有的 然后,您可以使用Diesel的 I have the following two functions: I want to combine both into one function. I have tried out a few different ways, but What are some ways to merge these two functions (which differ in only the fields These are my database struct definitions (the SQL schemas are equivalently defined): and the type of and the type of
First, let's start with a MCVE. This is a tool that professional programmers use when trying to understand a problem. It removes extraneous detail but provides enough detail for anyone to be able to pick it up and reproduce the situation. Compare how much code is present here that you didn't provide. Each missing piece is something that an answerer has to guess on as well as your time and their time generating.
Next, perform a diff between the two pieces of code to identify the differences. You stated: which differ in only the fields However, they differ in four locations. Just because something has the same prefix doesn't mean you can pass that prefix around. Modules aren't concepts that exist at runtime in Rust: Let's copy and paste one of the functions and replace all the unique values with dummies: This next part isn't pretty. Basically, the compiler will tell you every trait bound that isn't met, one-by-one. You "just" copy each error back to the code to set up all the constraints: This leads to the new error: You cannot assume any fields on a generic type, we need a trait: You: All together:
The next steps require a deeper dive into Diesel. The There's also a trait that wraps up all the
You can then make use of Diesel's
这篇关于如何将使用Diesel的多个功能合并为一个抽象?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
$ $ p $ pub fn get_most_recent_eth_entry(conn:& SqliteConnection) - >结果< i32,错误> {
let res = types :: ethereum :: table
.order(types :: ethereum :: time.desc())
.limit(1)
.load: :其中::类型ÐRecord GT;(&安培; * 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) - >结果< i32,错误> {
let res = types :: bitcoin :: table
.order(types :: bitcoin :: time.desc())
.limit(1)
.load: :其中::类型&BTCRecord GT;(&安培; * 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))
$ c
$ b
types :: ethereum
和 ETHRecord
合并为一个统一函数 get_most_recent_entry $ c $这些是我的数据库结构定义(SQL模式被等效地定义):
$ pre
$ b pre $ > #[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,
}
类型:: ethereum :: time是`database :: types :: __ diesel_infer_schema :: infer_bitcoin :: bitcoin :: columns :: time`
`types :: ethereum :: table`的类型是
`database :: types :: __ diesel_infer_schema: :infer_bitcoin :: bitcoin :: table`
[依赖关系]
diesel = {version =1.0.0-beta,features = [sqlite]}
#[macro_use]
extern crate diesel;
使用diesel :: prelude :: *;
使用diesel :: SqliteConnection;
mod类型{
表! {
比特币(时间){
时间 - > Int4,
}
}
表! {
ethereum(time){
time - > Int4,
}
}
#[derive(Insertable,Queryable,Debug)]
#[table_name =bitcoin]
pub struct BtcRecord {b $ b pub时间:i32,
}
#[派生(可插入,可查询,调试)]
#[table_name =ethereum]
pub struct EthRecord {
pub time:i32,
}
}
pub fn get_most_recent_eth_entry(conn:& SqliteConnection) - >结果< i32,String> {
let res = types :: ethereum :: table
.order(types :: ethereum :: time.desc())
.limit(1)
.load: :其中::类型&EthRecord GT;(&安培; * conn);在
match res {
Ok(x)=> {
if x.len()> 0 {
Ok(x.get(0).unwrap()。time)
} else {
Ok(0)
}
}
Err(err)=> Err(格式!(Error here!{:?},err)),
}
}
pub fn get_most_recent_btc_entry(conn:& SqliteConnection) - >结果< i32,String> {
let res = types :: bitcoin :: table
.order(types :: bitcoin :: time.desc())
.limit(1)
.load: :其中::类型&BtcRecord GT;(&安培; * 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))
$ c
$ b
types :: ethereum
和 ETHRecord
pre $ get_most_recent_eth_entry结果< i32,String> {
// ^^^^^^^^^^^^^^^^^^^^^^^^^
让res = types :: ethereum :: table
// ^^^^^^^^
.order(types :: ethereum :: time.desc())
// ^^^^^^^^
.limit( 1)
.load ::< types :: EthRecord>(& * conn);
// ^^^^^^^^^
pre $ pub fn get_most_recent_entry<'a,Tbl,Expr,Record>(
conn:& SqliteConnection,
表:Tbl,
时间:Expr,
) - >结果< 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,
) - >结果< i32,String>
其中
Expr:diesel :: ExpressionMethods,
Tbl:OrderDsl< Expr>> ;,
< Tbl as OrderDsl< Expr>>>: :输出:LimitDsl,
<< Tbl as OrderDsl< Expr>>> :: Output as LimitDsl> :: Output:RunQueryDsl< SqliteConnection> + Query,
Sqlite:HasSqlType<<<<< Tbl作为OrderDsl< Expr>>> :: Output as LimitDsl> :: Output as Query> :: SqlType> ;,
<<< Tbl as OrderDsl< Expr>>>> :: Output as LimitDsl> :: Output:QueryFragment< Sqlite>,
<< Tbl as OrderDsl< Expr>>> ; :: Output as LimitDsl>> :: Output:QueryId,
Record:Queryable<<<< Tbl as OrderDsl< Expr>>> :: Output as LimitDsl> :: Output as Query> ; :: SqlType,Sqlite> ;,
错误[E0609]:类型'& Record`没有字段`时间'
- > src / main.rs:64:38
|
64 |好的(x.get(0).unwrap()。time)
| ^^^^
pub trait时间{
fn time(& self) - > 123-132;
Record
所有在一起:
#[macro_use]
extern crate diesel;
使用diesel :: prelude :: *;
使用diesel :: SqliteConnection;
mod类型{
表! {
比特币(时间){
时间 - > Int4,
}
}
表! {
ethereum(time){
time - > Int4,
}
}
#[derive(Insertable,Queryable,Debug)]
#[table_name =bitcoin]
pub struct BtcRecord {b $ b pub时间:i32,
}
#[派生(可插入,可查询,调试)]
#[table_name =ethereum]
pub struct EthRecord {
pub time:i32,
}
}
pub trait时间{
fn time(& self) - > 123-132;
}
impl类型的时间:: EthRecord {
fn time(& self) - > i32 {
self.time
}
}
impl类型的时间:: BtcRecord {
fn time(& self) - > i32 {
self.time
}
}
使用diesel :: sqlite :: Sqlite;
使用diesel :: types :: HasSqlType;
使用diesel :: query_dsl :: methods :: {LimitDsl,OrderDsl};
使用diesel :: expression :: operators :: Desc;
使用diesel :: query_builder :: {Query,QueryFragment,QueryId};
使用diesel :: Queryable;
pub fn get_most_recent_entry<'a,Tbl,Expr,Record>(
conn:& SqliteConnection,
table:Tbl,
time:Expr,
) - >结果< i32,String>
其中
Expr:diesel :: ExpressionMethods,
Tbl:OrderDsl< Expr>> ;,
< Tbl as OrderDsl< Expr>>>: :输出:LimitDsl,
<< Tbl as OrderDsl< Expr>>> :: Output as LimitDsl> :: Output:RunQueryDsl< SqliteConnection> + Query,
Sqlite:HasSqlType<<<<< Tbl作为OrderDsl< Expr>>> :: Output as LimitDsl> :: Output as Query> :: SqlType> ;,
<<< Tbl as OrderDsl< Expr>>>> :: Output as LimitDsl> :: Output:QueryFragment< Sqlite>,
<< Tbl as OrderDsl< Expr>>> ; :: Output as LimitDsl>> :: Output:QueryId,
Record:Queryable<<<< Tbl as OrderDsl< 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(格式!(Error here!{:?},err)),
}
}
pub fn get_most_recent_eth_entry(conn:& SqliteConnection) - >结果< i32,String> {
get_most_recent_entry ::< _,_,types :: EthRecord>(
conn,
types :: ethereum :: table,
types :: ethereum :: time,
)
}
pub fn get_most_recent_btc_entry(conn:& SqliteConnection) - >结果< i32,String> {
get_most_recent_entry ::< _,_,types :: BtcRecord>(
conn,
types :: bitcoin :: table,
types :: bitcoin :: time,
)
}
helper_types
模块包含允许我们缩短界限的类型别名:
pub fn get_most_recent_entry<'a,Tbl,Expr,Record>(
conn:& SqliteConnection,
表:Tbl,
时间:Expr,
) - >结果< i32,String>
其中
Expr:diesel :: ExpressionMethods,
Tbl:OrderDsl
Order< Tbl,Desc< Expr>> ;: LimitDsl,
Limit< Order< Tbl,Desc< Expr>>> ;: RunQueryDsl< SqliteConnection>
+查询
+ QueryFragment< Sqlite>
+ QueryId,
Sqlite:HasSqlType<<< Limit< Order< Tbl,Desc< Expr>>>作为Query> :: SqlType> ;,
Record:Queryable<<< Order< Tbl,Desc< Expr>>>作为查询> :: SqlType,Sqlite> + Time,
Query *
-related subtraits: loadquery子
。使用它,我们可以将它缩减为:
pub fn get_most_recent_entry<'a,Tbl,Expr,Record>(
conn:& SqliteConnection,
table:Tbl,
time:Expr,
) - >结果< i32,String>
其中
Expr:diesel :: ExpressionMethods,
Tbl:OrderDsl
Order< Tbl,Desc< Expr>> ;: LimitDsl,
Limit< Order< Tbl,Desc< Expr>>>:LoadQuery< SqliteConnection,Record> ;,
记录:时间,
第一
函数和结果
s combinators缩短整个函数:
use diesel :: expression :: operators: :说明;
使用diesel :: helper_types :: {Limit,Order};
使用diesel :: query_dsl :: methods :: {LimitDsl,OrderDsl};
使用diesel :: query_dsl :: LoadQuery;
pub fn get_most_recent_entry<'a,Tbl,Expr,Record>(
conn:& SqliteConnection,
table:Tbl,
time:Expr,
) - >结果< i32,String>
其中
Expr:diesel :: ExpressionMethods,
Tbl:OrderDsl
Order< Tbl,Desc< Expr>> ;: LoadQuery< SqliteConnection,记录和GT; + LimitDsl,
Limit< Order< Tbl,Desc< Expr>>>:LoadQuery< SqliteConnection,Record> ;,
记录:时间,
{
表
.order(time.desc())
.first(conn)
.optional()
.map(| x | x.map_or(0,| x | x.time )))
.map_err(| e | format!(Error here!{:?},e))
}
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)),
}
}
types::ethereum
and ETHRecord
into one unified function get_most_recent_entry
? #[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`
[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)),
}
}
types::ethereum
and ETHRecord
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;
}
Record
.time()
in the method #[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,
)
}
helper_types
module contains type aliases that allow us to shorten the bounds: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*
-related subtraits: LoadQuery
. Using that, we can reduce it down to: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,
first
function and Result
s combinators to shorten the entire function: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))
}