Reputation: 21
I have an enum called Magnitude:
pub enum Magnitude<T> {
/// A finite value
Finite(T),
/// Positive infinity
PosInfinite,
/// Negative infinity
NegInfinite,
}
and I want to implement From
trait to be able to construct Magnitude from different types:
impl<T: Any> From<T> for Magnitude<T> {
fn from(value: T) -> Self {
if value.type_id() == TypeId::of::<f64>() {
unsafe {
let value_f64 = (&value as *const T).cast::<f64>().as_ref().unwrap().clone();
if value_f64 == f64::INFINITY {
Magnitude::PosInfinite
} else if value_f64 == f64::NEG_INFINITY {
Magnitude::NegInfinite
} else {
Magnitude::Finite(value)
}
}
} else if value.type_id() == TypeId::of::<f32>() {
unsafe {
let value_f32 = (&value as *const T).cast::<f32>().as_ref().unwrap().clone();
if value_f32 == f32::INFINITY {
Magnitude::PosInfinite
} else if value_f32 == f32::NEG_INFINITY {
Magnitude::NegInfinite
} else {
Magnitude::Finite(value)
}
}
} else {
Magnitude::Finite(value)
}
}
}
if value
is checked to be of type f64
using type_id
, how unsafe is it to cast it to f64?
it passes the test below but I don't know if it will work correctly all the time:
#[test]
fn f64_infinity() {
let pos_inf: Magnitude<f64> = f64::INFINITY.into();
let neg_inf: Magnitude<f64> = f64::NEG_INFINITY.into();
assert!(pos_inf.is_pos_infinite());
assert!(neg_inf.is_neg_infinite());
}
Upvotes: 2
Views: 1145
Reputation: 602215
A more natural way of implementing what you want would be to write different implementations for different types:
impl From<f32> for Magnitude<f32> {
fn from(value: f32) -> Self {
if value == f32::INFINITY {
Magnitude::PosInfinite
} else if value == f32::NEG_INFINITY {
Magnitude::NegInfinite
} else {
Magnitude::Finite(value)
}
}
}
However, it would be difficult to implement a blanket implementation if you already have concrete implementations
impl<T> From<T> for Magnitude<T> {
fn from(value: T) -> Self {
Magnitude::Finite(value)
}
}
results in
conflicting implementations of trait `std::convert::From<f32>` for type `Magnitude<f32>`:
--> src/lib.rs:22:1
|
10 | impl From<f32> for Magnitude<f32> {
| --------------------------------- first implementation here
...
22 | impl<T> From<T> for Magnitude<T> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Magnitude<f32>`
While there is an accepted RFC to support specialization in Rust, this has not yet been fully implemented. For the time being, I suggest a slightly less general approach:
macro_rules! magnitude_from_float_impl {
($t:ty) => {
impl From<$t> for Magnitude<$t> {
fn from(value: $t) -> Self {
if value.is_infinite() {
if value.is_sign_positive() {
Magnitude::PosInfinite
} else {
Magnitude::NegInfinite
}
} else {
Magnitude::Finite(value)
}
}
}
}
}
macro_rules! magnitude_from_impl {
($t:ty) => {
impl From<$t> for Magnitude<$t> {
fn from(value: $t) -> Self {
Magnitude::Finite(value)
}
}
}
}
magnitude_from_float_impl!(f32);
magnitude_from_float_impl!(f64);
magnitude_from_impl!(i64);
magnitude_from_impl!(i32);
This does not provide a blanket implementation, but it's easy to add further types to the list of supported types.
Upvotes: 3