我正在尝试实现一个结构来表示日期,它可以与范围语法 ( 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_one和replace_zero应该做什么难住了。文档说:
将此步骤替换为1, 返回自身。并将此步骤替换为0,返回自身。
难道我的结构需要有zero与one标识值只是在一个范围内使用?应该add_one不够吧?
文档使用的措辞也有点不清楚。如果我们替换x用1,并返回“本身”,就是“它”x还是1?
我只是查看了 Rust 的代码,其中使用了这些方法。整个 rustc 存储库的唯一用途是实现RangeInclusive操作。空RangeInclusive表示为从1到 的范围0,因此next,next_back和nth方法需要能够以某种方式获取这些内容,这就是replace_one和 的replace_zero用途。
我建议在 rustc 的 GitHub 上打开一个问题,以使文档变得更好,并可能更改这些方法的名称。