rust - 为特征实现通用函数,可以对不同的数字类型进行操作

问题描述 投票:0回答:1

是否有更好的方法来实现基本数字类型的通用转换函数?我一直在努力创建一个更通用的特征版本,它将 LE 字节流转换为特定类型,然后返回一个选项 float64。在此例程中,如果某个数字的具体类型为max Positive或max(positive-1),则无效。看起来这应该可以通过一个采用泛型并使用 where 子句的函数来实现,但我还没有找到一个可行的解决方案。


struct U16([u8;2]);
struct U32([u8;4]);
struct I16([u8;2]);
struct I32([u8;4]);

trait CanNumber {
    fn to_number(&self) -> Option<f64>;
}
impl CanNumber for U16 {
    fn to_number(&self) -> Option<f64> {
        type nt = u16;
        let num: nt = nt::from_le_bytes(self.0);
        if (num == nt::MAX) | (num == (nt::MAX-1)) {
            None
        }
        else {
            Some(num as f64)
        }
    }
}
impl CanNumber for I16 {
    fn to_number(&self) -> Option<f64> {
        type nt = i16;
        let num: nt = nt::from_le_bytes(self.0);
        if (num == nt::MAX) | (num == (nt::MAX-1)) {
            None
        }
        else {
            Some(num as f64)
        }
    }
}

fn main() {
    let test0: f64 = U16([1,2]).to_number().unwrap();
    let test1: f64 = I16([1,2]).to_number().unwrap();
    println!("test!");
}
generics rust types traits type-traits
1个回答
0
投票

这可能是一个很好的宏候选者。您可以通过编写一个特征,然后使用宏跨多种类型执行实现来处理这种情况。例如:

trait FromFooLittleEndian {
    type Bytes;
    type Number;

    fn from_foo_little_endian(bytes: Self::Bytes) -> Option<Self::Number>;
}

macro_rules! impl_from_little_endian {
    ( $( $ty:path, $bytes:literal );* $( ; )? ) => {
        $(
            impl FromFooLittleEndian for $ty {
                type Bytes = [u8; $bytes];
                type Number = $ty;

                fn from_foo_little_endian(bytes: Self::Bytes) -> Option<Self::Number> {
                    let num = <$ty>::from_le_bytes(bytes);
                    (num < <$ty>::MAX - 1).then_some(num)
                }
            }
        )*
    };
}

impl_from_little_endian!(
    u16, 2;
    i16, 2;
    u32, 4;
    i32, 4;
    u64, 8;
    i64, 8;
);

#[test]
fn foo_little_endian() {
    assert_eq!(Some(42), u16::from_foo_little_endian(42u16.to_le_bytes()));
    assert_eq!(None, u16::from_foo_little_endian(u16::MAX.to_le_bytes()));
    assert_eq!(
        None,
        u16::from_foo_little_endian((u16::MAX - 1).to_le_bytes())
    );
    assert_eq!(
        Some(u16::MAX - 2),
        u16::from_foo_little_endian((u16::MAX - 2).to_le_bytes())
    );
}

(游乐场)

请注意,此处的术语

foo
是此类字节流的应用程序特定名称的占位符,我建议在您的应用程序中将其替换为适当的名称。

© www.soinside.com 2019 - 2024. All rights reserved.