在Rust中实现通用的可递增特征

时间:2017-01-16 04:51:02

标签: rust traits generic-programming

我正在尝试了解如何在Rust中实现通用特征。

虽然我已经看过很多例子,但这些例子过于依赖特定用途(例如genomic mutators),以便我能够在Rust开发过程中理解这一点。

相反,这是一个基于相当普遍的东西的简单示例 - 递增:

trait Incrementable {
    fn post_inc(&mut self) -> Self;
    fn post_inc_by(&mut self, n: usize) -> Self;
}

impl Incrementable for usize {
    fn post_inc(&mut self) -> Self {
        let tmp = *self;
        *self += 1;
        tmp
    }

    //"Overload" for full generalizability
    fn post_inc_by(&mut self, n: usize) -> Self {
        let tmp = *self;
        *self += n;
        tmp
    }
}

fn main() {
    let mut result = 0;
    assert!(result.post_inc() == 0);
    assert!(result == 1);

    assert!(result.post_inc_by(3) == 1);
    assert!(result == 4);
}

上面的代码有效,但缺乏,因为如果没有编写大量的样板代码,它就无法推广到所有数字类型。

在我尝试概括上述代码的过程中,我已经开始使用类型系统,借用检查程序或者在我的通用版本中为我想要支持的每种类型实现FromPrimitive的实现{(1}}。有效地让我回到原点。)

您能否帮我理解如何一般地实施Incrementable,  这样post_inc()post_inc_by()至少适用于所有整数和浮点类型,理想情况下无需为每种类型编写实现?

我希望这个答案可以帮助我了解特征,实现,类型和相关类型如何在一个比我能够遇到的更简单的用例中协同工作。

我在Rust 1.16.0上。

3 个答案:

答案 0 :(得分:5)

@Simon Whitehead's example可以很容易地适应稳定的Rust:

trait Incrementable: Copy + std::ops::AddAssign<Self> {
    fn one() -> Self;

    fn post_inc(&mut self) -> Self {
        self.post_inc_by(Self::one())
    }

    fn post_inc_by(&mut self, n: Self) -> Self {
        let tmp = *self;
        *self += n;
        tmp
    }
}

impl Incrementable for u8  { fn one() -> Self {1} }
impl Incrementable for u16 { fn one() -> Self {1} }
impl Incrementable for u32 { fn one() -> Self {1} }
impl Incrementable for u64 { fn one() -> Self {1} }
impl Incrementable for i8  { fn one() -> Self {1} }
impl Incrementable for i16 { fn one() -> Self {1} }
impl Incrementable for i32 { fn one() -> Self {1} }
impl Incrementable for i64 { fn one() -> Self {1} }
impl Incrementable for f32 { fn one() -> Self {1.0} }
impl Incrementable for f64 { fn one() -> Self {1.0} }

虽然您需要为每种类型执行实现,但每种类型都非常简单。

您还可以使用宏来隐藏重复的实现:

macro_rules! impl_Incrementable{
    ($($m:ty),*) => {$( impl Incrementable for $m  { fn one() -> Self { 1 as $m } })*}
}

impl_Incrementable!{u8, u16, u32, u64, i8, i16, i32, i64, f32, f64}

答案 1 :(得分:3)

我们可以增加的类型需要

  1. 了解运营商和+=AddAssign
  2. 为“one”-element
  3. 定义一个值
  4. 可以复制,因为我们希望保留旧的未递增值。
  5. 第1点和第3点。我们可以通过使用特征限制来保证第2点。我们可以设置一个具有函数one() -> self的特征。

    所以这是一个有效的例子:

    // We need to know the operator "+="
    use std::ops::AddAssign;
    
    // The trait needs a type parameter
    trait Incrementable<T> {
        fn post_inc(&mut self) -> Self;
        fn post_inc_by(&mut self, n: T) -> Self;
    }
    
    // We need a trait which tells us the "one" value for a type
    trait Increment {
        fn one() -> Self;
    }
    
    // We need to implement the Increment trait for every type
    // we want to increment.
    impl Increment for usize {
        fn one() -> usize {
            1
        }
    }
    
    // Finally we implement the Increment trait generically for all types that
    // * know the operator "+=" AddAssign
    // * are copyable
    // * implement our Increment trait, so that we know their "one" value
    impl<T: AddAssign + Increment + Copy> Incrementable<T> for T {
        fn post_inc(&mut self) -> Self {
            let tmp = *self;
            *self += T::one();
            tmp
        }
    
        //"Overload" for full generalizability
        fn post_inc_by(&mut self, n: T) -> Self {
            let tmp = *self;
            *self += n;
            tmp
        }
    }
    
    fn main() {
        let mut result = 0;
        assert!(result.post_inc() == 0);
        assert!(result == 1);
    
        assert!(result.post_inc_by(3) == 1);
        assert!(result == 4);
    }
    

    您不必为每种类型编写Incrementable的实现,但您必须实现提供one()函数的特征。没有它你就无法逃脱,因为对于非数字类型而言,“逐一增加”意味着什么并不明显。

    我将所有内容保存在一般可以实现的通用实现中。例外是T::one(),因此除了每种类型的一个简单函数之外,不需要样板代码。

答案 2 :(得分:3)

您可以在what the std did之后使用宏执行此操作:

trait Incrementable {
    fn post_inc(&mut self) -> Self;
    fn post_inc_by(&mut self, n: Self) -> Self;
}

macro_rules! post_inc_impl {
    ($($t:ty)*) => ($(
        impl Incrementable for $t {
            fn post_inc(&mut self) -> Self {
                self.post_inc_by(1 as Self)
            }

            fn post_inc_by(&mut self, n: Self) -> Self {
                let tmp = *self;
                *self += n;
                tmp
            }
        }
    )*)
}

post_inc_impl! { usize u8 u16 u32 u64 isize i8 i16 i32 i64 f32 f64 }

fn main() {
    let mut result = 0;
    assert!(result.post_inc() == 0);
    assert!(result == 1);

    assert!(result.post_inc_by(3) == 1);
    assert!(result == 4);
}

如果您使用num crate

,则可以不使用宏
extern crate num;

use num::Num;

trait Incrementable<T: Num> {
    fn post_inc(&mut self) -> Self;
    fn post_inc_by(&mut self, n: T) -> Self;
}

impl<T: Num + std::ops::AddAssign<T> + Copy> Incrementable<T> for T {
    fn post_inc(&mut self) -> T {
        let tmp = *self;
        *self += T::one();
        tmp
    }

    fn post_inc_by(&mut self, n: T) -> Self {
        let tmp = *self;
        *self += n;
        tmp
    }
}