// 084: From and Into Traits
// Approach 1: Temperature conversion
#[derive(Debug, Clone, Copy)]
struct Celsius(f64);
#[derive(Debug, Clone, Copy)]
struct Fahrenheit(f64);
impl From<Celsius> for Fahrenheit {
fn from(c: Celsius) -> Self { Fahrenheit(c.0 * 9.0 / 5.0 + 32.0) }
}
impl From<Fahrenheit> for Celsius {
fn from(f: Fahrenheit) -> Self { Celsius((f.0 - 32.0) * 5.0 / 9.0) }
}
// Approach 2: Enum from string (TryFrom)
#[derive(Debug, PartialEq)]
enum Color { Red, Green, Blue }
impl TryFrom<&str> for Color {
type Error = String;
fn try_from(s: &str) -> Result<Self, Self::Error> {
match s {
"red" => Ok(Color::Red),
"green" => Ok(Color::Green),
"blue" => Ok(Color::Blue),
_ => Err(format!("Unknown color: {}", s)),
}
}
}
impl From<Color> for &str {
fn from(c: Color) -> Self {
match c { Color::Red => "red", Color::Green => "green", Color::Blue => "blue" }
}
}
// Approach 3: From for complex types
struct RawUser { name: String, age: String, email: String }
#[derive(Debug, PartialEq)]
struct User { name: String, age: u32, email: String }
impl TryFrom<RawUser> for User {
type Error = String;
fn try_from(raw: RawUser) -> Result<Self, Self::Error> {
let age = raw.age.parse().map_err(|_| "Invalid age".to_string())?;
Ok(User { name: raw.name, age, email: raw.email })
}
}
fn main() {
let boiling: Fahrenheit = Celsius(100.0).into();
println!("100C = {:.1}F", boiling.0);
let freezing: Celsius = Fahrenheit(32.0).into();
println!("32F = {:.1}C", freezing.0);
let color: Result<Color, _> = Color::try_from("red");
println!("color: {:?}", color);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_celsius_to_fahrenheit() {
let f: Fahrenheit = Celsius(100.0).into();
assert!((f.0 - 212.0).abs() < 0.001);
}
#[test]
fn test_fahrenheit_to_celsius() {
let c: Celsius = Fahrenheit(32.0).into();
assert!(c.0.abs() < 0.001);
}
#[test]
fn test_color_try_from() {
assert_eq!(Color::try_from("red"), Ok(Color::Red));
assert!(Color::try_from("purple").is_err());
}
#[test]
fn test_user_try_from() {
let raw = RawUser { name: "Alice".into(), age: "30".into(), email: "a@b.com".into() };
let user = User::try_from(raw).unwrap();
assert_eq!(user.age, 30);
}
#[test]
fn test_user_invalid() {
let raw = RawUser { name: "Bob".into(), age: "xyz".into(), email: "b@c.com".into() };
assert!(User::try_from(raw).is_err());
}
}
(* 084: From/Into โ OCaml conversion functions *)
(* Approach 1: Simple conversions *)
type celsius = Celsius of float
type fahrenheit = Fahrenheit of float
let fahrenheit_of_celsius (Celsius c) = Fahrenheit (c *. 9.0 /. 5.0 +. 32.0)
let celsius_of_fahrenheit (Fahrenheit f) = Celsius ((f -. 32.0) *. 5.0 /. 9.0)
(* Approach 2: String conversions *)
type color = Red | Green | Blue
let color_of_string = function
| "red" -> Ok Red
| "green" -> Ok Green
| "blue" -> Ok Blue
| s -> Error (Printf.sprintf "Unknown color: %s" s)
let string_of_color = function
| Red -> "red"
| Green -> "green"
| Blue -> "blue"
(* Approach 3: Complex record conversion *)
type raw_user = { raw_name: string; raw_age: string; raw_email: string }
type user = { name: string; age: int; email: string }
let user_of_raw raw =
match int_of_string_opt raw.raw_age with
| None -> Error "Invalid age"
| Some age -> Ok { name = raw.raw_name; age; email = raw.raw_email }
(* Tests *)
let () =
let (Fahrenheit f) = fahrenheit_of_celsius (Celsius 100.0) in
assert (abs_float (f -. 212.0) < 0.001);
let (Celsius c) = celsius_of_fahrenheit (Fahrenheit 32.0) in
assert (abs_float c < 0.001);
assert (color_of_string "red" = Ok Red);
assert (Result.is_error (color_of_string "purple"));
assert (string_of_color Blue = "blue");
let raw = { raw_name = "Alice"; raw_age = "30"; raw_email = "a@b.com" } in
assert (user_of_raw raw = Ok { name = "Alice"; age = 30; email = "a@b.com" });
let bad = { raw_name = "Bob"; raw_age = "xyz"; raw_email = "b@c.com" } in
assert (Result.is_error (user_of_raw bad));
Printf.printf "โ All tests passed\n"