The From and Into traits are Rust's standard way to express type conversions. They provide a consistent interface for transforming one type into another, making your code more flexible and easier to read. When you implement From, you automatically get Into for free thanks to a blanket implementation in the standard library.
The From trait defines a from() method that takes ownership of a value and returns the new type. It's typically used when the conversion is infallible (cannot fail). For fallible conversions, Rust provides TryFrom and TryInto (covered in a separate challenge).
pub trait From<T> {
fn from(value: T) -> Self;
}
pub trait Into<T> {
fn into(self) -> T;
}When you implement From<T> for U, you automatically get Into<U> for T. This means you should always implement From rather than Into directly.
// String implements From<&str>
let s: String = String::from("hello");
let s: String = "hello".into();
// Vec<u8> implements From<&str>
let bytes: Vec<u8> = Vec::from("hello");
// i64 implements From<i32>
let big: i64 = i64::from(42i32);
let big: i64 = 42i32.into();Implement the From trait for the following type conversions:
Create a Celsius struct and a Fahrenheit struct, both wrapping an f64. Implement conversions in both directions:
Fahrenheit::from(Celsius) - Formula: F = C × 9/5 + 32Celsius::from(Fahrenheit) - Formula: C = (F - 32) × 5/9Create an Rgb struct with r, g, b fields (all u8) and a HexColor struct wrapping a String. Implement:
HexColor::from(Rgb) - Convert RGB to hex string like "#FF5733"Create an Email struct wrapping a String. Implement:
Email::from(&str) - Create an email from a string sliceEmail::from(String) - Create an email from an owned stringCreate Point2D with x and y fields (f64) and Point3D with x, y, z fields (f64). Implement:
Point3D::from(Point2D) - Convert 2D point to 3D with z=0.0Create a generic Wrapper<T> struct that wraps a value. Implement:
Wrapper<T>::from(T) - Wrap any valueinto_inner(self) -> T to unwrap the value// Temperature conversion
let celsius = Celsius(100.0);
let fahrenheit: Fahrenheit = celsius.into();
assert!((fahrenheit.0 - 212.0).abs() < 0.001);
let fahrenheit = Fahrenheit(32.0);
let celsius: Celsius = fahrenheit.into();
assert!((celsius.0 - 0.0).abs() < 0.001);
// RGB to Hex
let rgb = Rgb { r: 255, g: 87, b: 51 };
let hex: HexColor = rgb.into();
assert_eq!(hex.0, "#FF5733");
// Email
let email: Email = "user@example.com".into();
assert_eq!(email.0, "user@example.com");
// Point conversion
let p2d = Point2D { x: 1.0, y: 2.0 };
let p3d: Point3D = p2d.into();
assert_eq!(p3d.z, 0.0);
// Generic wrapper
let wrapped: Wrapper<i32> = 42.into();
assert_eq!(wrapped.into_inner(), 42);From rather than Into - you get Into automaticallyformat!("{:02X}{:02X}{:02X}", r, g, b) for hex conversion (formats RGB values as 2-digit uppercase hex)Wrapper<T>, the From implementation should be generic over TFrom takes ownership of the input value.into() method can require type annotations if Rust can't infer the target typevalue.into::<TargetType>() or let binding let x: TargetType = value.into()