One more case for orphan rules: implement FromSql for 10 types

I have 10 enums, defined in not my crate, that I can not modify.
They implement TryFrom for &[u8] and I want to read them from rusqlite,
with row.get.

If it were enum defined in my crate, then one macro:

macro_rules! implement_from_sql_for_enums {
    ($err:ident, $err_map:tt, $($Enum:ty),*) => {
        $(
            impl FromSql for $Enum {
                #[inline]
                fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
                    Self::try_from(value.as_bytes()?).map_err(|$err| $err_map)
                }
            }
        )*
    }
}

implement_from_sql_for_enums!(
    err,
    { FromSqlError::Other(Box::new(err)) },
    Enum1,
    Enum2,
    ...
);

And all works. But what to do if enum defined in other, readonly, crate?

Should I define 10 new types, like struct MyEnum1(Enum1);,
and implement FromSql for them and Into to convert from MyEnum1 to Enum1,
to get syntax like this working:

let data: Enum1 = row.get(2)?.into();

Or there is better way?

I dislike MyEnum1 idea, because of AFAIK, I can not generate identifies in ordinary macros,
so I have to manually assign name for each new type, to cover that 10 enum cases.

1 Like

I'd definitely go with the newtype approach. Do not append arbitrary, meaningless suffixes (or prefixes) to your type names, though. Instead, simply call the newtype the same as the type it wraps, and refer to the inner type using its fully qualified path to disambiguate, i.e.:

struct MyType(other_library::some_module::MyType);

impl FromSql for MyType {
    ...
}

Better yet, if you know all of your types implement TryFrom<&[u8]>, then create a generic wrapper:

struct FromSqlBytes<T>(T);

impl<T> FromSql for FromSqlBytes<T>
where
    for<'a> T: TryFrom<&'a [u8]>,
    for<'a> <T as TryFrom<&'a [u8]>>::Error: Error + Send + Sync + 'static,
{
    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
        T::try_from(value.as_bytes()?)
            .map(FromSqlBytes)
            .map_err(|err| FromSqlError::Other(Box::new(err)))
    }
}

And then you can just use FromSqlBytes<LibType1>, etc. when you need it.

8 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.