Diesel AsExpression: why Option <-> Nullable needs to be implemented explicitly

156 Views Asked by At

Let's say I have a very simple Insertable struct containing a custom struct:

#[derive(Identifiable, Queryable, Insertable)]
#[table_name = "my_temp_table"]
#[primary_key(id)]
pub struct MyTempTable {
    pub id: i32,
    pub non_optional: MyText,
    pub optional: Option<MyText>,
}

pub struct MyText(pub String); 

And the corresponding schema:

table! {
    my_temp_table (id) {
        id -> Integer, 
        non_optional -> Text,
        optional -> Nullable<Text>,
    }
}

I would get an error :

error[E0277]: the trait bound `MyText: diesel::Expression` is not satisfied
  --> my_cool_file.rs:66:35
   |
66 | #[derive(Identifiable, Queryable, Insertable)]
   |                                   ^^^^^^^^^^ the trait `diesel::Expression` is not implemented for `MyText`
   |
   = help: the following other types implement trait `diesel::Expression`:
             Box<T>
             migrations_internals::schema::__diesel_schema_migrations::columns::star
             migrations_internals::schema::__diesel_schema_migrations::columns::version
             migrations_internals::schema::__diesel_schema_migrations::columns::run_on
           and 484 others
   = note: required for `MyText` to implement `diesel::expression::AsExpression<diesel::sql_types::Text>`
   = note: this error originates in the derive macro `Insertable` (in Nightly builds, run with -Z macro-backtrace for more info)

It makes sense, I need to implement AsExpression for MyText :

impl AsExpression<Text> for MyText {
    type Expression = AsExprOf<String, Text>;
    fn as_expression(self) -> Self::Expression {
        <String as AsExpression<Text>>::as_expression(self.0)
    }
}

impl<'a> AsExpression<Text> for &'a MyText {
    type Expression = AsExprOf<String, Text>;
    fn as_expression(self) -> Self::Expression {
        <String as AsExpression<Text>>::as_expression(self.0.to_owned())
    }
}

but even after doing that I still have a very similar error:

error[E0277]: the trait bound `MyText: diesel::Expression` is not satisfied
  --> my_cool_file.rs:66:35
   |
66 | #[derive(Identifiable, Queryable, Insertable)]
   |                                   ^^^^^^^^^^ the trait `diesel::Expression` is not implemented for `MyText`
   |
   = help: the following other types implement trait `diesel::Expression`:
             Box<T>
             migrations_internals::schema::__diesel_schema_migrations::columns::star
             migrations_internals::schema::__diesel_schema_migrations::columns::version
             migrations_internals::schema::__diesel_schema_migrations::columns::run_on
           and 484 others
   = note: required for `&'insert MyText` to implement `diesel::Expression`
   = note: required for `&'insert MyText` to implement `diesel::expression::AsExpression<diesel::sql_types::Nullable<diesel::sql_types::Text>>`
   = note: this error originates in the derive macro `Insertable` (in Nightly builds, run with -Z macro-backtrace for more info)

This confuses me as I would have expected the Option<Foo> <-> Nullable<SQLType> to work out of the box if Foo <-> SQLType works but I still need to implement

impl AsExpression<Nullable<Text>> for MyText 
impl<'a> AsExpression<Nullable<Text>> for &'a MyText 

And same for ToSql and FromSql traits if I need them.

Is it expected that I need to implement the Nullable case explicitly too or should it work out of the box and I missed something?

I am using Diesel 1.4.8.

1

There are 1 best solutions below

3
weiznich On

This behavior is somewhat expected. The documentation of ToSql states:

Any types which implement this trait should also #[derive(AsExpression)].

This derive would then generate all necessary implementations of AsExpression for you.

Now this derives is (as you already noticed) not really documented for diesel 1.4. Given that this diesel version is outdated for more than 2 years I would suggest that you update to diesel 2.x and then work with the documentation there. That documentation also contains a full list of impls that are generated.