Allow for generic functions that take BoxedSelectStatement as an arg #2936
Replies: 5 comments 1 reply
-
Your "workaround" is basically the correct solution. Let me first explain why this happens: As an additional note: The |
Beta Was this translation helpful? Give feedback.
-
Thanks for the help and advice. I've switched out
The function is now defined as:
(I've also tried using It's probably something simple I'm missing, so any pointers would be much appreciated. Regarding the public/private diesel API. As far as possible I'd like to rely only on public APIs that are going to continue to be available (though practically, as long as there is anything that provides a way to "conditionally modify a query, but need the type to remain the same", it will be easy enough to change need be). I'm now relying on I'll try to put together a minimal example to reproduce the unnamed type bound issue too. Thanks again for your help. |
Beta Was this translation helpful? Give feedback.
-
This likely indicates the following missing bounds for your function: T: Table + AsQuery,
T::Query: BoxedDsl<'static, Pg> (This is again one of the not so great rustc error messages. I think I've already reported that one and it seems like that is really hard to fix.)
|
Beta Was this translation helpful? Give feedback.
-
Well that was quite an adventure! Sorry it's taken me a while to reply — it's taken me quite a while to get it working, but it's not pretty:
I've tried adding various There could well be another trait that would dramatically simplify the above, but I spent some time searching and couldn't find one. Are there other traits like It really feels like there must be a better way. |
Beta Was this translation helpful? Give feedback.
-
I will use this post to write down a step by step description how I normally solve such generic issues. In the result this will lead to a much simpler solution. First let us use a "simplified" version of the OP's example: fn check_exists<'a, T>(
conn: &PgConnection,
check: dsl::IntoBoxed<'a, T, Pg>,
) -> Result<(), diesel::result::Error>
{
use diesel::dsl::{exists, select};
select(exists(check))
.get_result::<bool>(&conn)?
.then(|| {})
.expect("Does not exist");
Ok(())
} This changes a few minor bits, so that it is easily possible to test this function outside of the project of the OP. In detail, it changes the error type, it changes one line of error handling to a call to Now if we compile this version rustc emits the following error message:
This is one of the worser diesel error messages, but unfortunately nothing we can easily fix now. That written it mentions the bound fn check_exists<'a, T>(
conn: &PgConnection,
check: dsl::IntoBoxed<'a, T, Pg>,
) -> Result<(), diesel::result::Error>
+ where
+ T: BoxedDsl<'a, Pg>,
{
use diesel::dsl::{exists, select};
select(exists(check))
.get_result::<bool>(&conn)?
.then(|| {})
.expect("Does not exist");
Ok(())
}
This leads to the following compiler error message:
That's quite a lot of text, so let's try to split it down a bit. First of all, the overflow message it gone, so it seems like the previous change did help. That's good 🎉 Now let's go over the error messages one by one:
This one says that it is missing a certain trait bound ( fn check_exists<'a, T>(
conn: &PgConnection,
check: dsl::IntoBoxed<'a, T, Pg>,
) -> Result<(), diesel::result::Error>
where
T: BoxedDsl<'a, Pg>,
+ dsl::IntoBoxed<'a, T, Pg>: SelectQuery,
{
use diesel::dsl::{exists, select};
select(exists(check))
.get_result::<bool>(conn)?
.then(|| {})
.expect("Does not exist");
Ok(())
} Ok, one error message fixed, 7 error message remaining. Let's look at the next one:
This is basically like the last one, but with a different trait. The compiler suggests to add fn check_exists<'a, T>(
conn: &PgConnection,
check: dsl::IntoBoxed<'a, T, Pg>,
) -> Result<(), diesel::result::Error>
where
T: BoxedDsl<'a, Pg>,
- dsl::IntoBoxed<'a, T, Pg>: SelectQuery,
+ dsl::IntoBoxed<'a, T, Pg>: SelectQuery + diesel::expression::subselect::ValidSubselect<()>,
{
use diesel::dsl::{exists, select};
select(exists(check))
.get_result::<bool>(conn)?
.then(|| {})
.expect("Does not exist");
Ok(())
}
That fixes another error message. Let's have a look at the next one:
This one complains about a missing
This can be understand as if we add the bound fn check_exists<'a, T>(
conn: &PgConnection,
check: dsl::IntoBoxed<'a, T, Pg>,
) -> Result<(), diesel::result::Error>
where
T: BoxedDsl<'a, Pg>,
dsl::IntoBoxed<'a, T, Pg>: SelectQuery + ValidSubselect<()>,
+ dsl::Select<SelectStatement<()>, Exists<dsl::IntoBoxed<'a, T, Pg>>>: LoadQuery<PgConnection, bool>,
{
use diesel::dsl::{exists, select};
select(exists(check))
.get_result::<bool>(&conn)?
.then(|| {})
.expect("Does not exist");
Ok(())
}
This fixes another error message. This leaves 4 error message. The next two error messages are:
Note that:
That means we can just ignore them, as they are already fixed 🎉 Let's look at the next error message:
Again this one contains the same Leaves one error message:
That one is a bit strange. At this point we already changed quite a lot of details, by adding 3 additional trait bounds. That's the point where I normally just check and see which messages are fixed now and which are not. It turns the code compiles now, so it seems like the error message is already fixed by one of the former fixes 🎉 This leaves us with the following finial definition of your fn check_exists<'a, T>(
conn: &PgConnection,
check: dsl::IntoBoxed<'a, T, Pg>,
) -> Result<(), diesel::result::Error>
where
T: BoxedDsl<'a, Pg>,
dsl::IntoBoxed<'a, T, Pg>: SelectQuery + ValidSubselect<()>,
dsl::Select<SelectStatement<()>, Exists<dsl::IntoBoxed<'a, T, Pg>>>: LoadQuery<PgConnection, bool>,
{
use diesel::dsl::{exists, select};
select(exists(check))
.get_result::<bool>(&conn)?
.then(|| {})
.expect("Does not exist");
Ok(())
} |
Beta Was this translation helpful? Give feedback.
-
Firstly, it's entirely possible I'm just doing it wrong, so if there's a better way, please let me know.
I'm trying to create some helper functions to dry up my code a bit. In a number of places, I have access to a
BoxedSelectStatement
, and I'd like to be able to do something with it, for instance, check whether it exists. Something like the following:where for convenience:
However, I receive the following compile error:
I'm not exactly sure what this is telling me, but to some extent I would have expected that a
BoxedSelectStatement
would always work inside aselect(exists())
. Nonetheless, I can try adding a constraint to work around the problem as follows:And that works, except
SelectClause
isn't public, so I'm testing the above approach using a fork of diesel where I've addedpub use self::select_clause::SelectClause;
todiesel/src/query_builder/mod.rs
.Am I doing it wrong (i.e., is there something other than a
BoxedSelectStatement
I should be using for this use-case)?And if I'm not doing it wrong, should diesel provide trait implementation such that
QueryFragment<_>
for aBoxedSelectStatement
is guaranteed? Or is the approach of adding the constraint in the function the right way to go, and presumablySelectClause
should be made public?Beta Was this translation helpful? Give feedback.
All reactions