Search code examples
postgresqlrustrust-diesel

Diesel ORM queries return type unknown


I'm following the Getting Started with Diesel guide. I've completed it and it works correctly -- i.e. when I run the code I get the desired behavior.

However, I've noticed that many of the Diesel queries return unknown (or some type that wraps unknown), sort of as if the Rust language server isn't recognizing the model/schema type.

More concretely, I have the following model:

#[derive(Queryable, Selectable)]
#[diesel(table_name = posts)]
#[diesel(check_for_backend(diesel::pg::Pg))]
pub struct Post {
    pub id: i32,
    pub title: String,
    pub body: String,
    pub published: bool,
}

and the following schema (generated by Diesel CLI):

diesel::table! {
    posts (id) {
        id -> Int4,
        title -> Varchar,
        body -> Text,
        published -> Bool,
    }
}

I'm having this issue with all CRUD operations except create. Let's look at select as an example.

Here's the code snippet for querying posts by id:

let post = posts // typed correctly
    .find(post_id) // '.find()' exists, but return type wrong
    .select(Post::as_select()) // language server gives up
    .first(connection)
    .optional();

And here are my imports:

use self::models::Post;
use diesel::prelude::*;
use rust_playground::*;
use std::env::args;

The return type of the expression is Result<Option<{unknown}>, {unknown}>. The comments on each line of code reflect the correctness/completeness of the types as indicated by inlay type hints.

As you can see, posts correctly refers to the schema, and the find() function is a member of posts, but the return type of the find() call (<table as FindDsl<i32>>::Output) must be incorrect because it does not recognize the select() call that follows.

I'm getting similar behavior for other kinds of queries:

let results = posts // good so far
        .filter(published.eq(true)) // fine, but return type must be wrong because...
        .limit(5) // .limit() isn't recognized
        .select(Post::as_select()) // language server gives up
        .load(connection)
        .expect("Error loading posts");
let post = diesel::update(posts.find(id)) // seems fine, but return type must be wrong
        .set(published.eq(true)) // .set() not recognized
        .returning(Post::as_returning())
        .get_result(connection)
        .unwrap();

In all cases where I would expect to be able to chain another function, like .limit(), intellisense suggests only 4 functions: .as_sql(), .into_sql(), .into(), and .try_into(). So clearly these functions, e.g. .filter() are returning something related to querying databases, but for whatever reason not anything that would "allow" me to chain calls. I put "allow" in quotes because this is only a type issue; the code runs fine.

This is strange behavior because 1) I believe I've followed the guide to a T, and 2) I don't understand how the Diesel functions could be returning incorrect types.

What am I doing wrong? How can I get complete return types from Diesel queries?


Solution

  • As a regular user of Diesel, this is just how it is. Rust-analyzer has trouble with Diesel.

    I don't know if the issue lies with type-deduction stemming from the heavy use of complex traits, or from the macro-generated implementations. Likely a mix of both. The Rust-Analyzer maintainers are aware, many issues have been filed over the years with many resolved but some not.

    My only "solution" would be to try to get comfortable with Diesel's syntax without relying on your IDE tools and to encapsulate database operations into functions with well-defined parameters and return types such that the {unknown} types don't infect your experience with the rest of your code.