如何优化SKU多单位金额工具的Rust trait方法返回类型设计?
多单位SKU金额处理工具设计优化
核心逻辑
- 将多笔不同单位的金额转换为基础单位的单一金额
- 将基础单位的单一金额拆分为易读的多单位金额列表
示例代码
let five = "5kg".parse::<Amount>()?; let two = "2g".parse::<Amount>()?; let sum = five + two; let result = sum * 3; let result = Weight.reduce(result)?; assert_eq!(result, Amount::new(15006, Weight.base_unit())); let result = Weight.split(result)?.into_iter().collect::<Vec<_>>(); assert_eq!(result, [Amount::new(15, kg()), Amount::new(6, g())]);
当前设计
use std::ops::{Add, Mul}; trait Exchanger { type Err; fn rate(&self, unit: &Unit) -> Result<u32, Self::Err>; fn reduce<E>(&self, expr: E) -> Result<Amount, Self::Err> where for<'a> E: Reduce<&'a Self>, Self: Sized, { expr.reduce(self) } fn split<E>(&self, expr: E) -> Result<Split, Self::Err> where for<'a> E: Reduce<&'a Self>, Self: Sized, { let base = expr.reduce(self)?; todo!() } } impl<T: Exchanger> Exchanger for &T { type Err = T::Err; fn rate(&self, unit: &Unit) -> Result<u32, Self::Err> { (**self).rate(unit) } } trait Reduce<E: Exchanger> { fn reduce(self, e: E) -> Result<Amount, E::Err>; } trait Expression<E: Exchanger, Rhs, T>: Reduce<E> + Add<Rhs> + Mul<T> {} // impls Expression<E, Rhs, T> struct Amount(u32, Unit); struct Unit(String); // impls Expression<E, Rhs, T> struct Sum<L, R>(L, R); // impls Expression<E, Rhs, T> struct Split(Vec<Amount>);
现有问题
- 由于
Exchanger::split的返回类型可能是Amount或Sum,不得不额外定义Split结构体;尝试使用Box<dyn Expression>但因泛型参数无法在编译期确定而失败。 - 需要为新类型
Split重新实现Expressiontrait。 - 新增实现
Expression的类型时,必须为所有已实现Expression的类型实现Add<T>,组合数量呈爆炸式增长。 - 还需为引用类型重复实现所有相关trait。
优化方案
1. 拆分操作与运算解耦,移除冗余的Split结构体
拆分后的多单位列表本质是展示用的结果,而非可参与运算的表达式——如果需要继续运算,应先合并回基础单位。因此直接让Exchanger::split返回Vec<Amount>,不再将其纳入Expression体系:
fn split(&self, amount: Amount) -> Result<Vec<Amount>, Self::Err> { let base_unit = self.base_unit(); let mut remaining = amount.0; let mut result = Vec::new(); // 按单位从大到小拆分(如kg -> g) for (unit, rate) in self.units_descending() { if remaining >= rate { let count = remaining / rate; result.push(Amount(count, unit.clone())); remaining %= rate; } } if remaining > 0 { result.push(Amount(remaining, base_unit.clone())); } Ok(result) }
2. 简化Expression trait,避免泛型组合爆炸
将Expression的泛型参数改为关联类型,统一运算输出的约束,同时基于trait实现默认的Add/Mul操作:
trait Expression<E: Exchanger>: Reduce<E> { type AddOutput: Expression<E>; type MulOutput: Expression<E>; fn add(self, rhs: impl Expression<E>) -> Self::AddOutput; fn mul(self, rhs: u32) -> Self::MulOutput; } // 为Amount实现Expression impl<E: Exchanger> Expression<E> for Amount { type AddOutput = Sum<Amount, Amount>; type MulOutput = Amount; fn add(self, rhs: Amount) -> Self::AddOutput { Sum(self, rhs) } fn mul(self, rhs: u32) -> Self::MulOutput { Amount(self.0 * rhs, self.1.clone()) } } // 为Sum实现Expression,自动递归处理子表达式 impl<E: Exchanger, L: Expression<E>, R: Expression<E>> Expression<E> for Sum<L, R> { type AddOutput = Sum<Sum<L, R>, impl Expression<E>>; type MulOutput = Sum<L::MulOutput, R::MulOutput>; fn add(self, rhs: impl Expression<E>) -> Self::AddOutput { Sum(self, rhs) } fn mul(self, rhs: u32) -> Self::MulOutput { Sum(self.0.mul(rhs), self.1.mul(rhs)) } } // 基于Expression实现通用的Add/Mul操作符 impl<E: Exchanger, T: Expression<E>> Add for T { type Output = T::AddOutput; fn add(self, rhs: impl Expression<E>) -> Self::Output { Expression::add(self, rhs) } } impl<E: Exchanger, T: Expression<E>> Mul<u32> for T { type Output = T::MulOutput; fn mul(self, rhs: u32) -> Self::Output { Expression::mul(self, rhs) } }
新增Expression类型时,只需实现Expression trait即可自动获得Add/Mul能力,无需手动实现所有组合。
3. 自动实现引用类型的Reduce trait
通过泛型实现,为所有引用类型自动适配Reduce,避免重复代码:
impl<'a, E: Exchanger, T: Reduce<E> + 'a> Reduce<E> for &'a T { fn reduce(self, e: E) -> Result<Amount, E::Err> { (*self).reduce(e) } }
同时调整Exchanger::reduce的约束,兼容引用和所有权传递的场景:
fn reduce<E>(&self, expr: E) -> Result<Amount, Self::Err> where E: Reduce<&Self>, { expr.reduce(self) }
内容的提问来源于stack exchange,提问作者holi-java
相关产品推荐
相关产品推荐

