What is the right way to figure out what type is required here?

#1

I eventually figured this out, but through a long torturous process. What is the correct way to figure this out?

This line is almost correct, it uses diesel to return count of all records.

users.select(count_star()).first(&*conn).unwrap()

This is the error

error[E0282]: type annotations needed
  --> src/mylib.rs:20:29
   |
20 |     users.select(count_star()).first(&*conn).unwrap();                                                     
   |                                ^^^^^ cannot infer type for `U` 

Ok cool, I go to the documentation for first()
https://docs.diesel.rs/diesel/query_dsl/trait.RunQueryDsl.html#method.first

function sig pasted below

fn first<U>(self, conn: &Conn) -> QueryResult<U> 
where
    Self: LimitDsl,
    Limit<Self>: LoadQuery<Conn, U>, 

Ok good, but what are my options for U? Where do I go from here? I figure a “count” will be an integer so I try i32

users.select(count_star()).first::<i32>(&*conn).unwrap();                                                  

I get the error

error[E0277]: the trait bound `i32: diesel::deserialize::FromSql<diesel::sql_types::BigInt, _>` is not satisfied
  --> src/mylib.rs:20:29
   |
20 |     users.select(count_star()).first::<i32>(&*conn).unwrap();                                              
   |                                ^^^^^ the trait `diesel::deserialize::FromSql<diesel::sql_types::BigInt, _>` is not implemented for `i32`
   |
   = help: the following implementations were found:
             <i32 as diesel::deserialize::FromSql<diesel::sql_types::Integer, DB>>                              
   = note: required because of the requirements on the impl of `diesel::Queryable<diesel::sql_types::BigInt, _>`
for `i32`
   = note: required because of the requirements on the impl of `diesel::query_dsl::LoadQuery<_, i32>` for `diesel::query_builder::SelectStatement<schema::users::table, diesel::query_builder::select_clause::SelectClause<diesel::dsl::CountStar>, diesel::query_builder::distinct_clause::NoDistinctClause, diesel::query_builder::where_clause::NoWhereClause, diesel::query_builder::order_clause::NoOrderClause, diesel::query_builder::limit_clause::LimitClause<diesel::expression::bound::Bound<diesel::sql_types::BigInt, i64>>>`                  

Ok, so I look at the part that says

the trait `diesel::deserialize::FromSql<diesel::sql_types::BigInt, _>` is not implemented for `i32`

I look it up here
https://docs.diesel.rs/diesel/deserialize/trait.FromSql.html

I see the only match for i32 is for sqlite but I’m using mysql, i64 is what I can use!

impl<DB: Backend<RawValue = [u8]>> FromSql<BigInt, DB> for i64

Finally I’ve arrived:

users.select(count_star()).first::<i64>(&*conn).unwrap()                                               

What I’m trying to convey is that only through compiler errors could I get anywhere. Is this how its done? Did I miss a step in the docs somewhere?

#2

Knowing beforehand what you want is best. Realistic as you have done is typical.

From the docs you see that the generic type isn’t bound by the inputs. So the caller must specify it. The first function is just like standard into, parse, collect. Rather than code reuse that has traditionally been associated with generics they are reusing the name. Reading the code further you see the trait for the real function these end up calling. Multiple structures can implement the trait so as is often the case you need the specify the correct one you want.

1 Like
#3

Shouldn’t there be a trait bound on first?
It seems that U is limited somehow, but why isn’t rustc requiring it up front.

#4

The restrictions on U arise from this trait bound on RunQueryDsl::first since this is the only trait bound that mentions U:

Limit<Self>: LoadQuery<Conn, U>

If you click on LoadQuery, you find two implementations:

  • impl<Conn, T> LoadQuery<Conn, T> for SqlQuery
    where ...;
    

    Since you’re not calling sql_query, you are probably not getting SqlQuery.

  • impl<Conn, T, U> LoadQuery<Conn, U> for T
    where ..., U: Queryable<T::SqlType, Conn::Backend>;
    

    This second implementation appears to work for all other types of queries.

Therefore the constraint on U arises from Queryable. If you view the docs of that, you’ll find many implementations:

  • Some are specific to a database backend, e.g.

    impl<A, B, SA, SB> Queryable<Record<(SA, SB)>, Pg> for (A, B)
    where ...;
    

    But you’ll find that none of these specify concrete primitive types like integers or strings.

  • Some seem to be generic with respect to the backend, e.g.

    impl<__ST, __DB> Queryable<__ST, __DB> for i32
    where ..., Self: FromSql<__ST, __DB>;
    

    However, there is another bound that must be satisfied: Self: FromSql<__ST, __DB>. At this point, you can Ctrl+F and search for “for i32” on the docs for FromSql for the result type you want, as you’ve already figured out.


BTW, you can also annotate the type this way:

let i: i64 = users.select(count_star()).first(&*conn).unwrap();
#5

This is something I’d like to see us work on in the years ahead - instead of adding new features to rustc, improving things like type inference. I know it’s already happening with chalk and co. but we could do things like suggesting types that are valid, or telling the user if no types are valid. Also I think haskell has a thing where you can put a placeholder in and it guesses the type, I’m only thinking of this from the back of my mind so I may be wrong.

#6

I don’t even understand what I’m even looking at here. I think in everything I’ve read when you have a trait bounds like A:B the A is in the function sig somewhere. Limit just came out of nowhere.

Limit seems to be a type alias. doc

But its on the left side of the “where” clause and I don’t see how this

type Limit<Source> = <Source as LimitDsl>::Output;

relates to this in any understandable way

impl<Conn, T, U> LoadQuery<Conn, U> for T
where
    Conn: Connection,
    Conn::Backend: HasSqlType<T::SqlType>,
    T: AsQuery + RunQueryDsl<Conn>,
    T::Query: QueryFragment<Conn::Backend> + QueryId,
    U: Queryable<T::SqlType, Conn::Backend>,
{
    fn internal_load(self, conn: &Conn) -> QueryResult<Vec<U>> {
        conn.query_by_index(self)
    }
}

Edit: I’m hoping this is as bad as it gets and diesel is just pushing the limits.

#7

Pretty much.

#8

I think in everything I’ve read when you have a trait bounds like A:B the A is in the function sig somewhere.

Rust syntax is strange in that the Self parameter has a distinguished status in the syntax. However, when you see a trait bound such as A: F<B, C, D>, mentally you should just read it as a 4-argument predicate where A, B, C, and D are all arguments to F and are on (almost) equal footing:

F</* Self = */ A, B, C, D>

But its on the left side of the “where” clause and I don’t see how this […] relates to this in any understandable way […]

For answering the question of U, knowing what Limit<Self> is actually not all that important, because the implementation of LoadQuery is generic over the Self argument:

impl<Conn, T, U> LoadQuery<Conn, U> for T // ← generic T
where
    Conn: Connection,
    Conn::Backend: HasSqlType<T::SqlType>,
    T: AsQuery + RunQueryDsl<Conn>,
    T::Query: QueryFragment<Conn::Backend> + QueryId,
    U: Queryable<T::SqlType, Conn::Backend>;

If you just want to know what U needs to be, most of these trait bounds are (nearly) irrelevant:

    Conn: Connection,
    Conn::Backend: HasSqlType<T::SqlType>,
    T: AsQuery + RunQueryDsl<Conn>,
    T::Query: QueryFragment<Conn::Backend> + QueryId

since they don’t mention U at all. The only thing that matters is:

    U: Queryable<T::SqlType, Conn::Backend>

The T::SqlType should be the same as (or similar to) CountStar::SqlType (via some indirect chain of blanket implementations I’m too lazy to dig through):

impl Expression for CountStar {
    type SqlType = BigInt;
}

I don’t think there’s a way to figure out what Limit<Self> is from the docs alone because some of the concrete implementations of LoadQuery are kept hidden from the API docs. As far as the user is concerned, I believe the design of diesel is that the user should treat Limit<_> as an opaque data type (even though it’s really a type alias).

Whether the docs provide adequate information to figure out what’s going on, it’s debatable. This library really pushes the limits of the trait system IMO.