以下是针对长度单位的实现方案,包含完整的文件结构和详细实现:
- 文件结构更新
src/
└── units/
└── base/
├── length.rs # 基础长度单位
└── length/
├── metric.rs # 公制单位
├── imperial.rs # 英制单位
├── astronomical.rs # 天文单位
└── nautical.rs # 航海单位
- 基础长度单位定义 (src/units/base/length.rs)
use crate::{dimension::Unit, prefix::*};
/// 基础长度单位 - 米 (m)
pub type Meter<T> = Unit<T, 0, 1, 0, 0, 0, 0, 0, 0>;
/// 千米 (km)
pub type Kilometer<T> = Unit<T, KILO, 1, 0, 0, 0, 0, 0, 0>;
/// 厘米 (cm)
pub type Centimeter<T> = Unit<T, CENTI, 1, 0, 0, 0, 0, 0, 0>;
/// 毫米 (mm)
pub type Millimeter<T> = Unit<T, MILLI, 1, 0, 0, 0, 0, 0, 0>;
/// 提供长度单位通用功能
pub trait LengthUnit<T>: Sized {
/// 转换为米
fn to_meters(self) -> Meter<T>;
/// 从米创建
fn from_meters(meters: Meter<T>) -> Self;
/// 单位名称
fn unit_name() -> &'static str;
/// 单位符号
fn unit_symbol() -> &'static str;
}
// 为米实现特征
impl<T> LengthUnit<T> for Meter<T> {
fn to_meters(self) -> Meter<T> { self }
fn from_meters(meters: Meter<T>) -> Self { meters }
fn unit_name() -> &'static str { "meter" }
fn unit_symbol() -> &'static str { "m" }
}
- 公制单位细化 (src/units/base/length/metric.rs)
use super::super::*;
use crate::prefix::*;
/// 微米 (μm)
pub type Micrometer<T> = Unit<T, MICRO, 1, 0, 0, 0, 0, 0, 0>;
/// 纳米 (nm)
pub type Nanometer<T> = Unit<T, NANO, 1, 0, 0, 0, 0, 0, 0>;
/// 皮米 (pm)
pub type Picometer<T> = Unit<T, PICO, 1, 0, 0, 0, 0, 0, 0>;
/// 分米 (dm)
pub type Decimeter<T> = Unit<T, DECI, 1, 0, 0, 0, 0, 0, 0>;
// 实现转换方法
impl<T: num_traits::Float> Micrometer<T> {
pub fn to_millimeters(self) -> Millimeter<T> {
Millimeter::new(self.value / T::from(1000.0).unwrap())
}
}
impl<T: num_traits::Float> Nanometer<T> {
pub fn to_micrometers(self) -> Micrometer<T> {
Micrometer::new(self.value / T::from(1000.0).unwrap())
}
}
- 英制单位实现 (src/units/base/length/imperial.rs)
use super::super::*;
use crate::convert::ConvertTo;
/// 英寸 (in)
pub type Inch<T> = Unit<T, 0, 1, 0, 0, 0, 0, 0, 0, METRIC=false>;
/// 英尺 (ft)
pub type Foot<T> = Unit<T, 0, 1, 0, 0, 0, 0, 0, 0, METRIC=false>;
/// 码 (yd)
pub type Yard<T> = Unit<T, 0, 1, 0, 0, 0, 0, 0, 0, METRIC=false>;
/// 英里 (mi)
pub type Mile<T> = Unit<T, 0, 1, 0, 0, 0, 0, 0, 0, METRIC=false>;
impl<T: num_traits::Float> ConvertTo<Meter<T>> for Inch<T> {
fn convert(self) -> Meter<T> {
Meter::new(self.value * T::from(0.0254).unwrap())
}
}
impl<T: num_traits::Float> ConvertTo<Meter<T>> for Foot<T> {
fn convert(self) -> Meter<T> {
Meter::new(self.value * T::from(0.3048).unwrap())
}
}
// 添加显示实现
impl<T: std::fmt::Display> std::fmt::Display for Inch<T> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{} in", self.value)
}
}
- 天文单位实现 (src/units/base/length/astronomical.rs)
use super::super::*;
use crate::constants::Constants;
/// 天文单位 (AU)
pub type AstronomicalUnit<T> = Unit<T, 0, 1, 0, 0, 0, 0, 0, 0, SPECIAL=true>;
/// 光年 (ly)
pub type LightYear<T> = Unit<T, 0, 1, 0, 0, 0, 0, 0, 0, SPECIAL=true>;
/// 秒差距 (pc)
pub type Parsec<T> = Unit<T, 0, 1, 0, 0, 0, 0, 0, 0, SPECIAL=true>;
impl<T: num_traits::Float> AstronomicalUnit<T> {
pub fn to_meters(self) -> Meter<T> {
Meter::new(self.value * T::from(149_597_870_700.0).unwrap())
}
pub fn from_meters(meters: Meter<T>) -> Self {
Self::new(meters.value / T::from(149_597_870_700.0).unwrap())
}
}
impl<T: num_traits::Float> LightYear<T> {
pub fn to_meters(self) -> Meter<T> {
let c = Constants::<T>::new().speed_of_light;
let seconds_per_year = T::from(31_557_600.0).unwrap(); // 儒略年
Meter::new(self.value * c.value * seconds_per_year)
}
}
- 航海单位实现 (src/units/base/length/nautical.rs)
use super::super::*;
/// 海里 (nmi)
pub type NauticalMile<T> = Unit<T, 0, 1, 0, 0, 0, 0, 0, 0, SPECIAL=true>;
/// 链 (cable)
pub type Cable<T> = Unit<T, 0, 1, 0, 0, 0, 0, 0, 0, SPECIAL=true>;
/// 英寻 (fathom)
pub type Fathom<T> = Unit<T, 0, 1, 0, 0, 0, 0, 0, 0, SPECIAL=true>;
impl<T: num_traits::Float> NauticalMile<T> {
pub fn to_meters(self) -> Meter<T> {
Meter::new(self.value * T::from(1852.0).unwrap())
}
pub fn from_kilometers(km: Kilometer<T>) -> Self {
Self::new(km.value / T::from(1.852).unwrap())
}
}
- 单位转换宏 (src/units/base/length/mod.rs)
#[macro_export]
macro_rules! impl_length_conversion {
($from:ty, $to:ty, $factor:expr) => {
impl<T: num_traits::Float> ConvertTo<$to> for $from {
fn convert(self) -> $to {
<$to>::new(self.value * T::from($factor).unwrap())
}
}
impl<T: num_traits::Float> ConvertTo<$from> for $to {
fn convert(self) -> $from {
<$from>::new(self.value / T::from($factor).unwrap())
}
}
};
}
// 示例:毫米与厘米的互相转换
impl_length_conversion!(Millimeter<T>, Centimeter<T>, 0.1);
impl_length_conversion!(Centimeter<T>, Millimeter<T>, 10.0);
- 测试用例 (tests/unit_tests/length_tests.rs)
use super::super::*;
use approx::assert_relative_eq;
#[test]
fn test_metric_conversions() {
let km = Kilometer::new(1.5);
let m: Meter<_> = km.convert();
assert_relative_eq!(m.value, 1500.0);
let mm = Millimeter::new(2500.0);
let cm: Centimeter<_> = mm.convert();
assert_relative_eq!(cm.value, 250.0);
}
#[test]
fn test_imperial_conversions() {
let inches = Inch::new(12.0);
let feet: Foot<_> = inches.convert();
assert_relative_eq!(feet.value, 1.0);
let miles = Mile::new(1.0);
let meters: Meter<_> = miles.convert();
assert_relative_eq!(meters.value, 1609.344, epsilon = 1e-6);
}
#[test]
fn test_astronomical_units() {
let au = AstronomicalUnit::new(1.0);
let km: Kilometer<_> = au.to_meters().convert();
assert_relative_eq!(km.value, 149_597.870_700, epsilon = 1e-6);
}
关键改进说明
- 层级化分类:
-
将长度单位按使用场景分为公制、英制、天文、航海四类
-
每个类别有独立的实现文件
- 统一转换接口:
-
通过LengthUnit trait提供统一操作接口
-
使用宏简化重复的转换实现
- 精确的常数定义:
-
天文单位使用IAU2012定义值(149,597,870,700米)
-
海里使用国际标准定义(1852米)
- 增强的类型安全:
-
英制单位添加METRIC=false标记
-
特殊单位添加SPECIAL=true标记
- 完整的显示支持:
-
为每种单位实现Display trait
-
包含单位符号和名称
- 全面的测试覆盖:
-
测试所有类别的单位转换
-
使用approx处理浮点误差
这个实现方案既保持了编译期类型安全的优势,又通过模块化设计实现了:
-
更清晰的代码组织
-
更简便的单位扩展
-
更灵活的单位转换
-
更直观的单位显示