SOFe
SOFe

Reputation: 8214

Using newtypes in diesel columns

I am using newtypes like struct GuildId(i64); for the columns in my diesel model structs. Currently I am implementing these traits:

#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize)]
pub struct $name(pub i64);

impl AsExpression<BigInt> for $name { /* delegate to <i64 as AsExpression<BigInt> */ */ }

impl<ST, DB: Backend> Queryable<ST, DB> for $name
where i64: FromSql<ST, DB> { /* also delegate to i64 */

However, when I try to use this type in the following model structs:

#[derive(Associations, Identifiable, Queryable)]
#[belongs_to(Guild)]
struct Channel {
    guild_id: GuildId,
    // other fields
}

#[derive(Identifiable, Queryable)]
struct Guild {
    id: GuildId,
    // other fields
}

Channel still does not implement BelongingToDsl. When I try to cast it to the trait, it fails to compile with the following message:

error[E0277]: the trait bound `diesel::query_builder::select_statement::SelectStatement<webcord_schema::schema::channels::table>: diesel::query_dsl::filter_dsl::FilterDsl<diesel::expression::operators::Eq<webcord_schema::schema::channels::columns::guild_id, &webcord_schema::models::GuildId>>` is not satisfied
  --> src/index/guild.rs:23:32
   |
23 |                 let channels = <models::Channel as BelongingToDsl<&models::Guild>>::belonging_to(&guild)
   |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `diesel::query_dsl::filter_dsl::FilterDsl<diesel::expression::operators::Eq<webcord_schema::schema::channels::columns::guild_id, &webcord_schema::models::GuildId>>` is not implemented for `diesel::query_builder::select_statement::SelectStatement<webcord_schema::schema::channels::table>`
   |
   = help: the following implementations were found:
             <diesel::query_builder::select_statement::SelectStatement<F, S, D, W, O, L, Of, G, LC> as diesel::query_dsl::filter_dsl::FilterDsl<Predicate>>
   = note: required because of the requirements on the impl of `diesel::query_dsl::filter_dsl::FilterDsl<diesel::expression::operators::Eq<webcord_schema::schema::channels::columns::guild_id, &webcord_schema::models::GuildId>>` for `webcord_schema::schema::channels::table`

error[E0277]: the trait bound `webcord_schema::models::GuildId: diesel::expression::Expression` is not satisfied
  --> src/index/guild.rs:23:32
   |
23 |                 let channels = <models::Channel as BelongingToDsl<&models::Guild>>::belonging_to(&guild)
   |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `diesel::expression::Expression` is not implemented for `webcord_schema::models::GuildId`
   |
   = note: required because of the requirements on the impl of `diesel::expression::Expression` for `&webcord_schema::models::GuildId`
   = note: required because of the requirements on the impl of `diesel::expression::AsExpression<diesel::sql_types::BigInt>` for `&webcord_schema::models::GuildId`
   = note: required because of the requirements on the impl of `diesel::query_dsl::belonging_to_dsl::BelongingToDsl<&webcord_schema::models::Guild>` for `webcord_schema::models::Channel`

What traits am I missing?

Upvotes: 2

Views: 1240

Answers (1)

weiznich
weiznich

Reputation: 3435

That error is not related to BelongingToDsl, but to the incomplete implementation of the custom new type wrapper.

As the error message indicates you are missing a trait impl for your new type wrapper:

error[E0277]: the trait bound `webcord_schema::models::GuildId: diesel::expression::Expression` is not satisfied
  --> src/index/guild.rs:23:32
   |
23 |                 let channels = <models::Channel as BelongingToDsl<&models::Guild>>::belonging_to(&guild)
   |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `diesel::expression::Expression` is not implemented for `webcord_schema::models::GuildId`
   |
   = note: required because of the requirements on the impl of `diesel::expression::Expression` for `&webcord_schema::models::GuildId`
   = note: required because of the requirements on the impl of `diesel::expression::AsExpression<diesel::sql_types::BigInt>` for `&webcord_schema::models::GuildId`
   = note: required because of the requirements on the impl of `diesel::query_dsl::belonging_to_dsl::BelongingToDsl<&webcord_schema::models::Guild>` for `webcord_schema::models::Channel`

The interesting line is the second line in = note: required because of the requirements on the impl of diesel::expression::AsExpression<diesel::sql_types::BigInt> for '&webcord_schema::models::GuildId. This means you need to add at least an AsExpression<_> impl for a reference to your new type wrapper.

So now general speaking: There is this test case showing how to implement custom types in general. You will see that the custom type on rust side uses two custom derives (AsExpression and FromSqlRow) that are basically implementing the traits you've already implemented manually and additionally the missing ones. Additionally a ToSql/FromSql impl is required to describe how the type should be translated into/from a sql type.

Summing that up your type definition should probably look like:

#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, AsExpression, FromSqlRow)]
#[sql_type = "diesel::sql_types::BigInt")]
pub struct $name(pub i64);

impl<DB> ToSql<diesel::sql_types::BigInt, DB> for $name 
where DB: Backend,
    i64: ToSql<diesel::sql_types::BigInt, DB>,
{
    fn to_sql<W: Write>(&self, out: &mut Output<W, DB>) -> serialize::Result {
        <i64 as ToSql<diesel::sql_types::BigInt, DB>>::to_sql(&self.0, out)
    }
}

impl<DB> FromSql<diesel::sql_types::BigInt, DB> for $name 
where DB: Backend,
    i64: FromSql<diesel::sql_types::BigInt, DB>
{
    fn from_sql(bytes: Option<&DB::RawValue>) -> deserialize::Result<Self> {
        <i64 as FromSql<diesel::sql_types::BigInt, DB>>::from_sql(bytes).map($name)
    }
}

Upvotes: 3

Related Questions