# Typecast between structs?

Hi experts. How can I cast `struct T` to `struct U` with `as`, similar to

``````let a: u32 = 123;
let b: f32 = a as f32;
``````

I want to convert Celsius degree to Fahrenheit degree:

``````struct Celsius(f64);
struct Fahrenheit(f64);

fn main() {
let c = Celsius(37.3);
let f = c as Fahrenheit;   // how to make it possible?
}
``````

In C++, I can make it with the following

``````Fahrenheit::Fahrenheit(const Celsius& c) const {...}
Celsius::Celsius(const Fahrenheit& f) const {...}
/*
int main()
{
Celsius c(37.3);
Fahrenheit f1(c);
Fahrenheit f2 = (Fahrenheit)c;
Fahrenheit f3 = c;
...
}
*/
``````

I recommend you check out the `uom` crate.

1 Like

Syntactically, this is not supported. Semantically, this is usually done either with dedicated methods, or by implementing `From` trait.

6 Likes

The Rust equivalent of C++'s conversion constructor is the `From` trait.

You could define `Celsius` and `Fahrenheit` like this:

``````#[derive(Debug, Default, Copy, Clone, PartialEq, PartialOrd)]
pub struct Celsius(f64);

impl From<Fahrenheit> for Celsius {
fn from(f: Fahrenheit) -> Celsius {
Celsius((f.0 - 32.0) * 5.0 / 9.0)
}
}

#[derive(Debug, Default, Copy, Clone, PartialEq, PartialOrd)]
pub struct Fahrenheit(f64);

impl From<Celsius> for Fahrenheit {
fn from(c: Celsius) -> Fahrenheit {
Fahrenheit(c.0 * 9.0 / 5.0 + 32.0)
}
}
``````

And you then invoke the conversion explicitly (e.g. `Fahrenheit::from(celsius)`) or using the `into()` method from the `Into` trait that you get for free whenever `From` is implemented.

``````fn main() {
let c = Celsius(0.0);

// You can explicitly use the Fahrenheit::from() to do the conversion
let f = Fahrenheit::from(c);
assert_eq!(f, Fahrenheit(32.0));

// Or you can use the into() method and let type inference figure out which
// conversion to use
let equivalent_celsius: Celsius = f.into();
assert_eq!(equivalent_celsius, c);
}
``````

(playground)

You can think of the `as` keyword as a tool for coercing between different primitive types, so `bool -> u8`, `f32 -> u64`, `*const Foo -> *mut Bar`, and so on, and is deliberately not extensible because it'll do the corresponding compiler magic. The Rust Reference has a chapter on type cast expressions if you want to find out more.

8 Likes

Is Rust equivalent of C++'s parameterized constructor the `From` trait as well?

For example, in C++:

``````Celsius::Celsius(double value)
{
if( value < -273.15)
m_value = std::numeric_limits<double>::quiet_NaN();
else
m_value = value;
}
``````

In Rust:

``````impl From<f64> for Celsius {
fn from(value: f64) -> Celsius {
if value < -273.15 {
Celsius(f64::NAN)
} else {
Celsius(value)
}
}
}
``````

However, `let c: Celsius = Celsius(-275.0)` does not run into the function defined in the `From` trait. How can I make it similar to `Celsius c(-275.0)` in C++? Or how can I prevent users from calling `let c: Celsius = Celsius(-275.0)`?

Privacy. Don't mark the field as `pub` and only offer ways to construct or modify that uphold your invariants.

7 Likes

Thank you. That works.

Base on @quinedot's answer, you can also consider return `Option<Self>` from `fn new()` so all existing `Celsius` are valid temperature. Like this one.

1 Like

Normally you'll use the `TryFrom` trait for fallible conversions because it lets you return a descriptive error when the conversion fails.

Otherwise you might create a named constructor if it's an "interesting" conversion or the meaning may be ambiguous (e.g. `String::from_utf8()`) .

5 Likes

You should avoid using `as` conversion actually.
Using `into()` and `try_into().unwrap()` is better because they fail conversion instead of providing invalid value.
E.g.:

``````let a: u32 = 100000;
let b: u16 = a as u16; // Silently corrupts data

let b: u16 = a.into(); // Doesn't compile
let b: u16 = a.try_into().unwrap(); // panics at runtime

let a: u8 = 5;
let b: u32 = a.into(); // OK

let a: u32 = 5;
let b: u8 = a.try_into().unwrap(); // OK
``````
4 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.