Reputation: 189
I have an API written in Rust and it's goal is to expose ~15 tables in a database. I've written several very similar functions to expose each table so I thought I'd take a crack at polymorphism to simplify the code.
I've reduced all the code to a single file:
#[macro_use]
extern crate diesel;
extern crate dotenv;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
table! {
table1 (id) {
id -> Int4,
value -> Text,
}
}
table! {
table2 (id) {
id -> Int4,
value -> Text,
}
}
#[derive(Identifiable, Queryable, Serialize)]
#[table_name = "table1"]
struct Model1 {
pub id: i32,
pub value: String,
}
#[derive(Identifiable, Queryable, Serialize)]
#[table_name = "table2"]
struct Model2 {
pub id: i32,
pub value: String,
}
use dotenv::dotenv;
use std::env;
fn get_connection() -> PgConnection {
dotenv().ok();
let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
PgConnection::establish(&database_url).expect("Database not working")
}
use diesel::QueryDsl;
use diesel::pg::PgConnection;
use diesel::query_dsl::LoadQuery;
use diesel::result::Error;
use diesel::query_dsl::filter_dsl::FindDsl;
use serde::Serialize;
use serde_json::to_string;
fn get_row<'a, Model, Table>(table: Table, id: i32) -> Result<String, Error>
where
Table: QueryDsl,
<Table as FindDsl<i32>>::Output: LoadQuery<PgConnection, Model>,
Model: Serialize,
{
let result: Model = table.find(id).load(&get_connection())?;
Ok(to_string(&result)?)
}
fn main() {
let row1 = get_row::<Model1, _>(table1::table, 1);
let row2 = get_row::<Model2, _>(table2::table, 1);
}
This is my Cargo.toml
file
[package]
name = "question"
version = "0.1.0"
[dependencies]
diesel = { version = "*", features = ["postgres"] }
diesel_codegen = "*"
serde = "*"
serde_derive = "*"
serde_json = "*"
dotenv = "*"
When I try to run this, I get the following compiler error:
error[E0275]: overflow evaluating the requirement `<Table as diesel::query_dsl::filter_dsl::FilterDsl<_>>::Output`
--> src/main.rs:54:1
|
54 | / fn get_row<'a, Model, Table>(table: Table, id: i32) -> Result<String, Error>
55 | | where
56 | | Table: QueryDsl,
57 | | <Table as FindDsl<i32>>::Output: LoadQuery<PgConnection, Model>,
... |
61 | | Ok(to_string(&result)?)
62 | | }
| |_^
|
= help: consider adding a `#![recursion_limit="128"]` attribute to your crate
The compiler tells me that I might resolve the issue by increasing the recursion limit, but I tried doing that up to 8096 and the error still wasn't resolved.
Upvotes: 6
Views: 1020
Reputation: 430961
You weren't far off:
use diesel::dsl::Find;
use diesel::pg::PgConnection;
use diesel::query_dsl::{LoadQuery, RunQueryDsl};
use diesel::query_dsl::filter_dsl::FindDsl;
use diesel::result::Error;
fn get_row<'a, Model, Table>(table: Table, id: i32) -> Result<String, Error>
where
Table: FindDsl<i32>,
Find<Table, i32>: LoadQuery<PgConnection, Model>,
{
let conn = get_connection();
let result = table.find(id).load::<Model>(&conn)?;
unimplemented!()
}
Points to note:
Diesel has a bunch of helper type aliases which make writing trait bounds easier. Here, the Find
alias has been used.
It can be more obvious to use more specific trait bounds instead of less specific ones. Here, switching to FindDsl
instead of QueryDsl
is probably what makes the code compile. QueryDsl
doesn't mean that you can call find
as the QueryDsl::find
method actually has further trait bounds:
fn find<PK>(self, id: PK) -> Find<Self, PK>
where
Self: FindDsl<PK>,
The return value of load
is a Vec
of results, but you had annotated the type as a single value. Perhaps you wanted to use get_result
or first
instead?
I removed the Serde-specific code because there's no obvious way to convert the Serde error to a Diesel error; that's up to you.
Upvotes: 5