我如何在Rust 1.25中正确实现std :: iter :: Step?

时间:2018-02-04 22:08:34

标签: rust

我正在尝试实现一个表示日期的结构,可以使用范围语法(for d in start..end { })。我知道已经存在处理日期的板条箱,但我这样做是为了练习。

这是结构:

type DayOfMonth = u8;
type Month = u8;
type Year = u16;

#[derive(PartialEq, Eq, Clone)]
pub struct Date {
    pub year: Year,
    pub month: Month,
    pub day: DayOfMonth
}

以下是我想要使用它的方法:

fn print_dates() {
    let start = Date { year: 1999, month: 1, day: 1 };
    let end = Date { year: 1999, month: 12, day: 31 };

    for d in start..end {
        println!("{}-{}-{}", d.year, d.month, d.day);
    }
} 

我最初尝试实现Iterator特性,但是当我尝试使用范围语法时,我收到编译错误,说我需要实现Step

文档显示Step特征的此签名。

pub trait Step: PartialOrd<Self> + Clone {
    fn steps_between(start: &Self, end: &Self) -> Option<usize>;
    fn replace_one(&mut self) -> Self;
    fn replace_zero(&mut self) -> Self;
    fn add_one(&self) -> Self;
    fn sub_one(&self) -> Self;
    fn add_usize(&self, n: usize) -> Option<Self>;
}

我已经实施了OrdPartialOrd

impl Ord for Date {
    fn cmp(&self, other: &Self) -> Ordering {
         match self.year.cmp(&other.year) {
            Ordering::Equal =>
                match self.month.cmp(&other.month) {
                    Ordering::Equal =>
                        self.day.cmp(&other.day),
                    ord => ord
                },
            ord => ord
        }
    }
}

impl PartialOrd for Date {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

我正在使用Clone自动实施#[derive(Clone)]

我开始实现Step,但有一些方法我无法弄清楚该怎么做。以下是我到目前为止的情况:

impl Step for Date {
    fn steps_between(start: &self, end: &self) -> Option<usize> {
        //is_valid_date checks that the month is not > 12 and other rules like that
        if is_valid_date(start) && is_valid_date(end) {
            //get_epoch_day_number gets the number of days since 1900-01-01
            let diff = get_epoch_day_number(end) - get_epoch_day_number(start);
            Some(diff)
        }
        else { None }
    }

    fn add_one(&self) -> Self {
        //Try the next day
        let mut next = Date {
            year: self.year,
            month: self.month,
            day: self.day + 1
        };
        //If not valid, try the 1st of the next month
        if !is_valid_date(&next) {
            next = Date {
                year: self.year,
                month: self.month + 1,
                day: 1
            };
        }
        //If not valid, try the 1st of the next year
        if !is_valid_date(&next) { 
            next = Date {
                year: self.year + 1,
                month: 1,
                day: 1
            };
        }
        next
    }

    fn sub_one(&self) -> Self {
        //Try the prev day
        let mut prev = Date {
            year: self.year,
            month: self.month,
            day: self.day - 1
        };
        //If not valid, try the last of the prev month
        if !is_valid_date(&prev) {
            let m = self.month - 1;
            prev = Date {
                year: self.year,
                month: m,
                day: get_month_length(self.year, m)
            };
        }
        //If not valid, try the last of the prev year
        if !is_valid_date(&prev) {             
            prev = Date {
                year: self.year - 1,
                month: 12,
                day: 31
            };
        }
        prev
    }

    fn add_usize(&self, n: usize) -> Self {
        //This is really inefficient, but that's not important
        let mut result = self;
        for i in 1..n+1 {
            result = result.add_one();
        }
        result
    }

    fn replace_one(&mut self) -> Self {
        // ?
    }

    fn replace_zero(&mut self) -> Self {
        // ?
    }
}

我对replace_onereplace_zero应该做的事情感到非常难过。文档说:

1替换此步骤,返回自身。0替换此步骤,返回自身。

我的结构体是否需要zeroone标识值才能在范围内使用? add_one不应该足够吗?

文档中使用的措辞也有点不清楚。如果我们将x替换为1并返回“本身”,那么“是”x还是1

1 个答案:

答案 0 :(得分:1)

我只是看了Rust的代码,其中使用了这些方法。 The only uses in whole of rustc's repository将实施RangeInclusive次操作。空RangeInclusive表示为从10的范围,因此nextnext_backnth方法需要能够以某种方式得到这些,这就是replace_onereplace_zero的用途。

我建议在rustc的GitHub上打开一个问题,以使文档更好,并可能更改这些方法的名称。