如何从复合多个参数的类型别名推断参数类型(uom crate)

问题描述 投票:0回答:1

我有这个板条箱,它定义了一个基本类型 Quantity 和它的几个类型别名。例如,输入长度 = 数量。如何定义一个泛型函数或一个包装 Quantity 的结构,但仅将这些别名之一作为类型参数,而不必重新实现三个类型参数。

我想要成功实施的是以下内容:

struct ParsedQuantity<Q> {
    quantity: Q,
    additional_field: Something
}

fn parse_value<Q>() -> Result<ParsedQuantity<Q>, &str>;

其中 Q 代表这些类型别名之一,这样我就可以做到:


struct Model {
    mass: ParsedQuantity<Mass>
}
let mass = parse_value::<Mass>("1 kg");

如果我直接返回数量,即使我的函数签名是 parse_value,由于 rust 类型推断,它也会在某种程度上起作用。

pub fn parse_value<D, U, V>(s: &str) -> Result<Quantity<D, U, V>, &str>
where
    D: Dimension + ?Sized,
    U: uom::si::Units<V> + ?Sized,
    V: uom::Conversion<V> + uom::num_traits::Num,
    Quantity<D, U, V>: FromStr,
{ ... }

let parsed: Length = parse_value("1 m").unwrap();

这里 D、U、V 已正确推断。

但是当尝试创建返回结构的版本时,无法再以方便的方式推断类型。我必须手动指定三个类型参数中的每一个,这不是我想要的。

我可以使用什么类型魔法才能将别名 Q = Quantity 作为函数或结构中的单个类型参数,而不是 3 个参数 D、U、V?

generics rust struct type-inference
1个回答
0
投票

感谢评论中的 kmdreko。事实证明,这不是你可以在 Rust 中做的事情,但是它足以将单个参数类型限制为我需要的特征。现在我的数量模块在仍在对其进行操作时没有实际提及数量

它看起来像这样:

#[derive(Debug)]
pub struct ParsedValue<L>
where
    L: FromStr + Debug + DefaultUnit,
{
    pub raw: String,
    pub parsed: L,
}

impl<L> ParsedValue<L>
where
    L: FromStr + Debug + DefaultUnit,
    <L as FromStr>::Err: Debug,
{
    // Constructor to create a new ParsedValue
    pub fn new(raw: &str) -> Result<Self, ParseError<L>> {
        if let Some(captures) = HAS_UNIT_RE.captures(raw) {
            let _is_reference = captures.get(1).is_some();
            let raw = format!(
                "{} {}",
                captures[2].to_string(),
                if let Some(unit) = captures.get(3) {
                    unit.as_str()
                } else {
                    L::DEFAULT_UNIT
                }
            );
            // Parse the string into L, or handle failure
            let parsed = raw
                .parse::<L>()
                .map_err(|e| ParseError::UnrecognizedQuantity(e))?;
            Ok(ParsedValue {
                raw: raw.to_string(),
                parsed,
            })
        } else {
            Err(ParseError::InvalidQuantityFormat(raw.to_string()))
        }
    }
}

use uom::si::f64 as si;

pub trait DefaultUnit {
    const DEFAULT_UNIT: &str;
}

/// Length (default: kilometers, since distances in geoscience are often measured in km)
pub type Length = ParsedValue<si::Length>;

impl DefaultUnit for si::Length {
    const DEFAULT_UNIT: &str = "km";
}


#[derive(Debug)]
pub enum ParseError<T> where T: FromStr, <T as FromStr>::Err: Debug {
    InvalidQuantityFormat(String),
    UnrecognizedQuantity(<T as FromStr>::Err)
}

use ParseError::*;

impl<T> Display for ParseError<T> where T: FromStr, <T as FromStr>::Err: Debug  {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "{}",
            match self {
                InvalidQuantityFormat(s) => format!("Invalid Quantity Format : {}", s),
                UnrecognizedQuantity(s) => format!("Unrecognized quantity : '{s:?}'. Check the unit and value.")
            }
        )
    }
}

impl<T> std::error::Error for ParseError<T> where  T: FromStr+Debug, <T as FromStr>::Err: Debug {
}


© www.soinside.com 2019 - 2024. All rights reserved.