为u128自定义Diesel包装类型时遇编译错误求助
问题解决:Diesel中u128类型的PostgreSQL适配
需求背景
为Diesel默认未支持的u128类型创建自定义类型U128,目标是将原生u128以numeric类型(优先)或文本类型存储到PostgreSQL数据库中。
现有实现代码
自定义U128类型实现
use std::io::Write; use diesel::{AsExpression, deserialize, FromSqlRow, serialize}; use diesel::deserialize::{FromSql}; use diesel::pg::{Pg}; use diesel::serialize::{IsNull, Output, ToSql}; use diesel::sql_types::{Text}; #[derive(AsExpression, FromSqlRow, Debug, Clone, Copy)] #[diesel(sql_type = Text)] pub struct U128(pub u128); impl ToSql<Text, Pg> for U128 { fn to_sql<'b>(&self, out: &mut Output<'b, '_, Pg>) -> serialize::Result { write!(out, "{}", self.0.to_string())?; Ok(IsNull::No) } } impl FromSql<Text, Pg> for U128 { fn from_sql(bytes: Option<&[u8]>) -> deserialize::Result<Self> { let s = String::from_utf8_lossy(bytes.as_bytes()); Ok(U128(s.parse()?)) } }
Diesel结构体定义
#[derive(Queryable, Selectable)] #[diesel(table_name = crate::schema::balance)] #[diesel(check_for_backend(diesel::pg::Pg))] pub struct Balance { pub id: i32, pub account_name: String, pub balance: u128, }
编译错误信息
error[E0277]: the trait bound `u128: diesel::Queryable<diesel::sql_types::Text, Pg>` is not satisfied --> src\models.rs:117:27 | 117 | pub balance: u128, | ^^^^ the trait `diesel::Queryable<diesel::sql_types::Text, Pg>` is not implemented for `u128` | = help: the following other types implement trait `diesel::Queryable<ST, DB>`: i8 i16 i32 i64 u8 u16 u32 u64 and 2 others = note: required for `u128` to implement `FromSqlRow<diesel::sql_types::Text, Pg>` = help: see issue #48214
问题分析与解决方案
核心问题
你定义了自定义U128类型,但结构体Balance中依然使用原生u128作为字段类型。Diesel没有为原生u128实现对应PostgreSQL文本类型的Queryable trait,导致编译失败。另外当前自定义类型用的是Text sql类型,没有满足你优先用numeric存储的需求。
修正步骤
切换到numeric类型存储
PostgreSQL的numeric类型支持超大整数,更适合存储u128。修改自定义类型的sql类型为Numeric,并实现对应的序列化/反序列化逻辑:use diesel::sql_types::Numeric; use bigdecimal::BigDecimal; #[derive(AsExpression, FromSqlRow, Debug, Clone, Copy)] #[diesel(sql_type = Numeric)] pub struct U128(pub u128); impl ToSql<Numeric, Pg> for U128 { fn to_sql<'b>(&self, out: &mut Output<'b, '_, Pg>) -> serialize::Result { let decimal = BigDecimal::from(self.0); decimal.to_sql(out) } } impl FromSql<Numeric, Pg> for U128 { fn from_sql(bytes: Option<&[u8]>) -> deserialize::Result<Self> { let decimal = BigDecimal::from_sql(bytes)?; let value = decimal.to_u128()?; Ok(U128(value)) } }注意:需要在
Cargo.toml中添加bigdecimal依赖,Diesel的Numeric类型依赖该库处理数值转换。修改结构体字段类型
将Balance结构体中的balance字段类型从u128改为自定义的U128:#[derive(Queryable, Selectable)] #[diesel(table_name = crate::schema::balance)] #[diesel(check_for_backend(diesel::pg::Pg))] pub struct Balance { pub id: i32, pub account_name: String, pub balance: U128, }同步数据库schema
确保数据库表balance中的balance字段类型为numeric(而非text),可以通过Diesel的迁移文件修改:ALTER TABLE balance ALTER COLUMN balance TYPE numeric;
额外优化
如果需要在代码中方便地在u128和U128之间转换,可以实现From和Into trait:
impl From<u128> for U128 { fn from(value: u128) -> Self { U128(value) } } impl From<U128> for u128 { fn from(value: U128) -> Self { value.0 } }
这样就可以直接用U128::from(123)或者let num: u128 = balance.balance.into()来转换。
内容的提问来源于stack exchange,提问作者a_dizzle
相关产品推荐
相关产品推荐

