You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何优化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重新实现Expression trait。
  • 新增实现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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 14:23:09