Beginner Projects
Unit Conversion Tool
A multi-domain unit conversion tool converts measurements across different physical dimensions—such as Length, Mass, Data Storage, and Time—while enforcing dimensional analysis so incompatible conversions (such as converting Kilograms to Meters) are rejected with a clear dimensional mismatch error.
Dimensional Base-Unit Conversion Architecture:
Source Quantity `(value, from_unit)`
|
v 1. Check `from_unit.dimension() == to_unit.dimension()`
| If mismatch (e.g., `Length != Mass`) ---> `Err(DimensionMismatch)`!
|
v 2. Convert to Base SI Unit (`base = value * from_unit.to_base_factor()`)
| - Length Base: Meter (`m`)
| - Mass Base: Gram (`g`)
| - Data Storage Base: Byte (`B`)
|
v 3. Convert from Base SI Unit (`result = base / to_unit.to_base_factor()`)
Supported Dimensions and Base Conversion Factors
| Dimension | Base Unit (factor = 1.0) |
Supported Units & Exact Base Factors |
|---|---|---|
| Length | Meter (m) |
mm ($0.001$), cm ($0.01$), m ($1.0$), km ($1000.0$), in ($0.0254$), ft ($0.3048$), mi ($1609.344$) |
| Mass | Gram (g) |
mg ($0.001$), g ($1.0$), kg ($1000.0$), oz ($28.349523125$), lb ($453.59237$) |
| Data | Byte (B) |
B ($1.0$), KiB ($1024.0$), MiB ($1048576.0$), GiB ($1073741824.0$) |
Complete Runnable Implementation
use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Dimension {
Length,
Mass,
DataSize,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Unit {
// Length
Millimeter,
Centimeter,
Meter,
Kilometer,
Inch,
Foot,
Mile,
// Mass
Milligram,
Gram,
Kilogram,
Ounce,
Pound,
// DataSize
Byte,
Kibibyte,
Mebibyte,
Gibibyte,
}
impl Unit {
pub fn parse(symbol: &str) -> Option<Self> {
match symbol.trim() {
"mm" => Some(Unit::Millimeter),
"cm" => Some(Unit::Centimeter),
"m" => Some(Unit::Meter),
"km" => Some(Unit::Kilometer),
"in" => Some(Unit::Inch),
"ft" => Some(Unit::Foot),
"mi" => Some(Unit::Mile),
"mg" => Some(Unit::Milligram),
"g" => Some(Unit::Gram),
"kg" => Some(Unit::Kilogram),
"oz" => Some(Unit::Ounce),
"lb" => Some(Unit::Pound),
"B" => Some(Unit::Byte),
"KiB" => Some(Unit::Kibibyte),
"MiB" => Some(Unit::Mebibyte),
"GiB" => Some(Unit::Gibibyte),
_ => None,
}
}
pub const fn dimension(self) -> Dimension {
match self {
Unit::Millimeter
| Unit::Centimeter
| Unit::Meter
| Unit::Kilometer
| Unit::Inch
| Unit::Foot
| Unit::Mile => Dimension::Length,
Unit::Milligram | Unit::Gram | Unit::Kilogram | Unit::Ounce | Unit::Pound => {
Dimension::Mass
}
Unit::Byte | Unit::Kibibyte | Unit::Mebibyte | Unit::Gibibyte => Dimension::DataSize,
}
}
/// Factor to multiply by to convert `1.0` of `self` into the dimension's base unit (`m`, `g`, or `B`).
pub const fn to_base_factor(self) -> f64 {
match self {
Unit::Millimeter => 0.001,
Unit::Centimeter => 0.01,
Unit::Meter => 1.0,
Unit::Kilometer => 1000.0,
Unit::Inch => 0.0254,
Unit::Foot => 0.3048,
Unit::Mile => 1609.344,
Unit::Milligram => 0.001,
Unit::Gram => 1.0,
Unit::Kilogram => 1000.0,
Unit::Ounce => 28.349_523_125,
Unit::Pound => 453.592_37,
Unit::Byte => 1.0,
Unit::Kibibyte => 1024.0,
Unit::Mebibyte => 1024.0 * 1024.0,
Unit::Gibibyte => 1024.0 * 1024.0 * 1024.0,
}
}
pub const fn symbol(self) -> &'static str {
match self {
Unit::Millimeter => "mm",
Unit::Centimeter => "cm",
Unit::Meter => "m",
Unit::Kilometer => "km",
Unit::Inch => "in",
Unit::Foot => "ft",
Unit::Mile => "mi",
Unit::Milligram => "mg",
Unit::Gram => "g",
Unit::Kilogram => "kg",
Unit::Ounce => "oz",
Unit::Pound => "lb",
Unit::Byte => "B",
Unit::Kibibyte => "KiB",
Unit::Mebibyte => "MiB",
Unit::Gibibyte => "GiB",
}
}
}
#[derive(Debug, PartialEq, Eq)]
pub enum ConversionError {
UnknownUnit(String),
DimensionMismatch {
from: Unit,
to: Unit,
from_dim: Dimension,
to_dim: Dimension,
},
}
impl fmt::Display for ConversionError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ConversionError::UnknownUnit(u) => write!(f, "unrecognized unit '{u}'"),
ConversionError::DimensionMismatch {
from,
to,
from_dim,
to_dim,
} => write!(
f,
"cannot convert {} ({from_dim:?}) to {} ({to_dim:?})",
from.symbol(),
to.symbol()
),
}
}
}
pub fn convert_value(value: f64, from: Unit, to: Unit) -> Result<f64, ConversionError> {
if from.dimension() != to.dimension() {
return Err(ConversionError::DimensionMismatch {
from,
to,
from_dim: from.dimension(),
to_dim: to.dimension(),
});
}
let base_value = value * from.to_base_factor();
Ok(base_value / to.to_base_factor())
}
fn main() {
// 1. Length conversion: 5 miles -> kilometers
let km = convert_value(5.0, Unit::Mile, Unit::Kilometer).unwrap();
assert!((km - 8.04672).abs() < 1e-5);
println!("5.0 mi = {km:.4} km");
// 2. Data size conversion: 2.5 GiB -> MiB
let mib = convert_value(2.5, Unit::Gibibyte, Unit::Mebibyte).unwrap();
assert_eq!(mib, 2560.0);
println!("2.5 GiB = {mib:.0} MiB");
// 3. Dimensional mismatch check (`kg` -> `km` is rejected!):
let mismatch = convert_value(10.0, Unit::Kilogram, Unit::Kilometer).unwrap_err();
println!("Dimensional check caught: {mismatch}");
}
5.0 mi = 8.0467 km
2.5 GiB = 2560 MiB
Dimensional check caught: cannot convert kg (Mass) to km (Length)