Luuk Wester
Luuk Wester

Reputation: 189

Generic function using Diesel causes overflow

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

Answers (1)

Shepmaster
Shepmaster

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:

  1. Diesel has a bunch of helper type aliases which make writing trait bounds easier. Here, the Find alias has been used.

  2. 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>,
    
  3. 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?

  4. 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

Related Questions