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

Jam*_*aix 6 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
}
Run Code Online (Sandbox Code Playgroud)

这是我想如何使用它:

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);
    }
} 
Run Code Online (Sandbox Code Playgroud)

我最初尝试实现Iteratortrait,但是当我尝试使用范围语法时,我收到一个编译器错误,提示我需要实现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>;
}
Run Code Online (Sandbox Code Playgroud)

我已经实施Ord并且PartialOrd

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))
    }
}
Run Code Online (Sandbox Code Playgroud)

我正在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 {
        // ?
    }
}
Run Code Online (Sandbox Code Playgroud)

我真的被什么replace_onereplace_zero应该做什么难住了。文档说:

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

难道我的结构需要有zeroone标识值只是在一个范围内使用?应该add_one不够吧?

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

mca*_*ton 2

我只是查看了 Rust 的代码,其中使用了这些方法。整个 rustc 存储库的唯一用途是实现RangeInclusive操作。空RangeInclusive表示为从1到 的范围0,因此next,next_backnth方法需要能够以某种方式获取这些内容,这就是replace_one和 的replace_zero用途。

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