我有这个板条箱,它定义了一个基本类型 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
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
感谢评论中的 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 {
}