簡體   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