繁体   English   中英

rust 实现多种类型的特征

[英]rust implement trait for multiple types

我正在尝试一次为多种类型实现特征,我发现避免代码重复的唯一方法是将所有类型转换为一个通用结构并为该结构实现特征,如下所示。

trait Increment {
    fn increment(&self) -> Option<String>;
}

struct NumberWrapper {
    number: String,
}

impl Increment for NumberWrapper {
    fn increment(&self) -> Option<String> {
        let num: Result<u64, _> = self.number.parse();
        match num {
            Err(_) => None,
            Ok(x) => Some((x + 1).to_string())
        }
    }
}

impl<T> From<T> for NumberWrapper where T: ToString {
    fn from(input: T) -> NumberWrapper {
        NumberWrapper { number: input.to_string() }
    }
}

fn main() {
    let number_u8: u8 = 10;
    println!("number_u8 is: {}", NumberWrapper::from(number_u8).increment().unwrap());
    let number_u32: u16 = 10;
    println!("number_u16 is: {}", NumberWrapper::from(number_u32).increment().unwrap());
    let number_u32: u32 = 10;
    println!("number_u32 is: {}", NumberWrapper::from(number_u32).increment().unwrap());
    let number_u64: u64 = 10;
    println!("number_u64 is: {}", NumberWrapper::from(number_u64).increment().unwrap());
}

有没有其他方法可以做到这一点?

我想到了两种更优雅地做到这一点的方法。 首先,我猜你宁愿让你的特质看起来像这样:

trait Increment {
    // It would probably be better to take `self` by value if you
    // just want this for numeric types which are cheaply copied,
    // but I'll leave it for generality.
    fn increment(&self) -> Option<Self> where Self: Sized;
}

我会假设这会向前发展(但请纠正我)。

第一种方法使用一个非常简单的

macro_rules! impl_increment {
    ($($t:ty),*) => {
        $(
            impl Increment for $t {
                fn increment(&self) -> Option<Self> {
                    self.checked_add(1)
                }
            }
        )*
    }
}

它只匹配一个规则,该规则读取用逗号分隔的任意数量的类型,并根据 Rust 中的数字原语的checked_add方法实现该类型的increment方法。 你可以这样称呼它:

// This will create an impl block for each of these types:
impl_increment!{u8, u16, u32, u64, i8, i16, i32, i64}

fn main() {
    let x = 41u32;
    assert_eq!(x.increment(), Some(42));
    let y = -60_000i64;
    assert_eq!(y.increment(), Some(-59_999));
    let z = 255u8;
    assert_eq!(z.increment(), None);
}

或者,您可以通过转换为通用类型和从通用类型转换来类似于您已经在做的事情。 在这种情况下,通过使用所有比u64窄的无符号 integer 类型实现的Into<u64>TryFrom<u64>特征:

use std::convert::TryFrom;

impl<T> Increment for T
where T: Copy + Into<u64> + TryFrom<u64>
{
    fn increment(&self) -> Option<Self> {
        let padded: u64 = (*self).into();
        TryFrom::try_from(padded + 1).ok()
    }
}

fn main() {
    let x = 41u32;
    assert_eq!(x.increment(), Some(42));
    let y = 60_000u64;
    assert_eq!(y.increment(), Some(60_001));
    let z = 255u8;
    assert_eq!(z.increment(), None);
}

这有更多的运行时开销,并且不能很好地概括(例如,不适用于签名的 integer 类型)。 所以我会用宏路由 go 。

一揽子实现可用于为满足某些其他特征的所有类型实现特征。 我不确定您的示例中的特征到底是什么意思,但我希望下面的示例说明了这个想法。

use std::ops::Add;
use num::traits::One;

trait Increment {
    fn increment(&self) -> Option<String>;
}

impl<T> Increment for T
    where T: Add + Copy + One,
          <T as Add>::Output: ToString,
{
    fn increment(&self) -> Option<String> {
        Some((*self + One::one()).to_string())
    }
}

fn main() {
    let number_u8: u8 = 10;
    println!("number_u8 is: {}", number_u8.increment().unwrap());
    let number_u32: u16 = 10;
    println!("number_u16 is: {}", number_u32.increment().unwrap());
    let number_u32: u32 = 10;
    println!("number_u32 is: {}", number_u32.increment().unwrap());
    let number_u64: u64 = 10;
    println!("number_u64 is: {}", number_u64.increment().unwrap());
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM