I have a MRE repo here: https://github.com/hansl/mre-issue-higher-ranked
Essentially, I'm using a Rocket handler function like so:
#[get("/")]
async fn index(mut db: Db) -> String {
let query = schema::posts::table.into_boxed();
let (items, total): (Vec<models::Post>, i64) = query
.paginate(Some(0))
.load_and_count_total(&mut db)
.await
.unwrap();
format!("{items:?} {total}\n")
}
I'm defining the paginate
and load_and_count_total
functions as follow (in the pages.rs
module):
pub trait Paginate: Sized {
fn paginate(self, page: Option<i64>) -> Paginated<Self>;
}
impl<T: Query> Paginate for T {
fn paginate(self, page: Option<i64>) -> Paginated<Self> {
let page = page.unwrap_or(0);
Paginated {
query: self,
per_page: DEFAULT_PER_PAGE,
page,
offset: page * DEFAULT_PER_PAGE,
}
}
}
// ...
impl<T> Paginated<T> {
pub async fn load_and_count_total<'a, U>(
self,
conn: &mut AsyncPgConnection,
) -> QueryResult<(Vec<U>, i64)>
where
Self: LoadQuery<'a, AsyncPgConnection, (U, i64)>,
U: Send,
T: 'a,
{
// Ignore those linting errors. `get(0)` cannot be replaced with `first()`.
#![allow(clippy::get_first)]
let results = self.load::<(U, i64)>(conn).await?;
let total = results.get(0).map(|x| x.1).unwrap_or(0);
let records = results.into_iter().map(|x| x.0).collect();
Ok((records, total))
}
}
The error given while compiling is:
error: implementation of `diesel::query_builder::Query` is not general enough
--> src/main.rs:12:1
|
12 | #[get("/")]
| ^^^^^^^^^^^ implementation of `diesel::query_builder::Query` is not general enough
|
= note: `diesel::query_builder::Query` would have to be implemented for the type `BoxedSelectStatement<'0, (Integer, Text), FromClause<table>, Pg>`, for any lifetime `'0`...
= note: ...but `diesel::query_builder::Query` is actually implemented for the type `BoxedSelectStatement<'1, (Integer, Text), FromClause<table>, Pg>`, for some specific lifetime `'1`
= note: this error originates in the attribute macro `get` (in Nightly builds, run with -Z macro-backtrace for more info)
I've tried a lot of various combinations of lifetimes in load_and_count_total
and specifying or restricting types, I just cannot get this to work. Best I could do is remove the into_boxed()
which fixes the error, but I need it as the conditions get exponentially complex in my real world original code and I need to be able to conditionally filter my queries.
The actual code is much more complex, but I've tried to lower it to the simplest form of reproduction I could get. All modules except main.rs
and pages.rs
can be ignored in the repo.
I would expect load_and_count_total
to work as expected without a compiler error about different lifetimes.
Doing some research leads me to believe this is a compiler error that's been documented in issues that are still opened, but it seems there also might be a solution that doesn't involve fixing the compiler. I just cannot find it for my specific code.
Okay after a few days (and hours after playing with the MRE) of investigating this specific issue, I figured a way to tell the compiler that my code is safe.
Instead of declaring load_and_count_total
as async
, I changed the declaration to the following:
impl<T: Query> Paginated<T> {
pub fn load_and_count_total<'a, U>(
self,
conn: &'a mut AsyncPgConnection,
) -> impl std::future::Future<Output = QueryResult<(Vec<U>, i64)>> + Send + 'a
where
Self: LoadQuery<'a, AsyncPgConnection, (U, i64)>,
U: Send + 'a,
T: 'a,
{
// Ignore those linting errors. `get(0)` cannot be replaced with `first()`.
#![allow(clippy::get_first)]
let results = self.load::<(U, i64)>(conn);
async move {
let results = results.await?;
let total = results.get(0).map(|x| x.1).unwrap_or(0);
let records = results.into_iter().map(|x| x.0).collect();
Ok((records, total))
}
}
}
Notice the extra + Send + 'a
limitations on the return type, which let the compiler know that the Future is alive for the whole duration of 'a
, and the Send
. Seems that both are necessary for the compiler to stop complaining, but neither are actually inferred by the compiler automatically if the function is declared async
.
This fixes the issue above.