As a practice exercise, I'm trying to create a temperature unit library.
Currently, the following code won't compile and the error message is unclear to me. The problem seems to be a type conflict.
type Fahrenheit = f64;
type Celcius = f64;
type Kelvin = f64;
trait TemperatureUnit {
fn to_kelvin(&self) -> Kelvin;
}
impl TemperatureUnit for Fahrenheit {
fn to_kelvin(&self) -> Kelvin {
(*self + 459.67) * 5/9
}
}
impl TemperatureUnit for Celcius {
fn to_kelvin(&self) -> Kelvin {
*self + 273.15
}
}
impl TemperatureUnit for Kelvin {
fn to_kelvin(&self) -> Kelvin {
*self
}
}
Errors:
error[E0119]: conflicting implementations of trait `TemperatureUnit` for type `f64`
--> src/lib.rs:18:1
|
12 | impl TemperatureUnit for Fahrenheit {
| ----------------------------------- first implementation here
...
18 | impl TemperatureUnit for Celcius {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `f64`
error[E0119]: conflicting implementations of trait `TemperatureUnit` for type `f64`
--> src/lib.rs:24:1
|
12 | impl TemperatureUnit for Fahrenheit {
| ----------------------------------- first implementation here
...
24 | impl TemperatureUnit for Kelvin {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `f64`
I'm new to Rust, so maybe this is not the proper methodology to achieve a compiler-enforced type-safe unit conversion.
When you write type
, you're making a type alias, not a new type. It's like when you use typedef
in C++, for instance.
type Fahrenheit = f64;
This literally says "when I say 'Fahrenheit', pretend I said f64
instead". Not a new type, not a distinct structure, literally the same. So you just implemented TemperatureUnit for f64
three times.
If you want to wrap f64
in a custom type under your control, you're looking for the newtype pattern .
pub struct Fahrenheit(pub f64);
Now Fahrenheit
is a distinct structure that happens to be isomorphic to f64
. You can go from f64
to Fahrenheit
with the Fahrenheit(x)
constructor, and you can go back with the my_fahrenheit.0
projection. You can also name the projection if you prefer.
pub struct Fahrenheit { pub float_repr: f64 };
Rust will almost certainly compile this internally to an actual f64
. Structs with only one field will generally have no overhead. But we can actually guarantee this with repr(transparent)
#[repr(transparent)]
pub struct Fahrenheit(pub f64);
This guarantees that, at runtime, Fahrenheit
and f64
have the same representation (even to the extent that transmute
is defined between them), while at compile-time they're distinct types.
The only thing left to decide is what traits you want your new type to implement. It will inherit nothing from f64
by default, so you'll likely have a massive derive(...)
clause right before the declaration including Eq
, Ord
, Clone
, Copy
, etc. (non-exhaustive list; you know your use case better than I do)
The problem here is that type
just aliases a type, it doesn't create a new one. Rust is complaining because those three impl
blocks are trying to implement TemperatureUnit
on the same type three times.
Instead, you should declare new types. The example below uses struct
.
struct Fahrenheit(f64);
struct Celcius(f64);
struct Kelvin(f64);
trait TemperatureUnit {
fn to_kelvin(&self) -> Kelvin;
}
impl TemperatureUnit for Fahrenheit {
fn to_kelvin(&self) -> Kelvin {
Kelvin((self.0 + 459.67) * 5.0/9.0)
}
}
impl TemperatureUnit for Celcius {
fn to_kelvin(&self) -> Kelvin {
Kelvin(self.0 + 273.15)
}
}
impl TemperatureUnit for Kelvin {
fn to_kelvin(&self) -> Kelvin {
Kelvin(self.0)
}
}
fn main()
{
println!("Hey");
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.