Reputation: 907
Today I try to solve a problem on LeetCode. This is my code (Playground):
#[test]
fn basic_test() {
assert_eq!(day_of_year("2019-01-09".to_string()), 9);
assert_eq!(day_of_year("2019-02-10".to_string()), 41);
assert_eq!(day_of_year("2003-03-01".to_string()), 60);
assert_eq!(day_of_year("2004-03-01".to_string()), 61);
}
pub fn day_of_year(date: String) -> i32 {
let vec: Vec<&str> = date.split("-").collect();
[(vec[0],vec[1],vec[2])].iter().map(|(year,month,day)|
match month {
&"01" => day.parse().unwrap(),
&"02" => day.parse().unwrap() + 31,
_ => match year.parse().unwrap(){
y if y%4==0&&y%100!=0
||y%400==0&&y%3200!=0
||y%172800==0=>
match month {
&"03" => day.parse().unwrap()+31+29,
&"04" => day.parse().unwrap()+31+29+31,
&"05" => day.parse().unwrap()+31+29+31+30,
&"06" => day.parse().unwrap()+31+29+31+30+31,
&"07" => day.parse().unwrap()+31+29+31+30+31+30,
&"08" => day.parse().unwrap()+31+29+31+30+31+30+31,
&"09" => day.parse().unwrap()+31+29+31+30+31+30+31+31,
&"10" => day.parse().unwrap()+31+29+31+30+31+30+31+31+30,
&"11" => day.parse().unwrap()+31+29+31+30+31+30+31+31+30+31,
&"12" => day.parse().unwrap()+31+29+31+30+31+30+31+31+30+31+30
},
_ => match month{
&"03" => day.parse().unwrap()+31+28,
&"04" => day.parse().unwrap()+31+28+31,
&"05" => day.parse().unwrap()+31+28+31+30,
&"06" => day.parse().unwrap()+31+28+31+30+31,
&"07" => day.parse().unwrap()+31+28+31+30+31+30,
&"08" => day.parse().unwrap()+31+28+31+30+31+30+31,
&"09" => day.parse().unwrap()+31+28+31+30+31+30+31+31,
&"10" => day.parse().unwrap()+31+28+31+30+31+30+31+31+30,
&"11" => day.parse().unwrap()+31+28+31+30+31+30+31+31+30+31,
&"12" => day.parse().unwrap()+31+28+31+30+31+30+31+31+30+31+30
}
}
}
).collect()
}
I think the code can self-explain. I'm getting this error message:
error[E0277]: a collection of type `i32` cannot be built from an iterator over elements of type `_`
--> src/lib.rs:45:7
|
45 | ).collect()
| ^^^^^^^ a collection of type `i32` cannot be built from `std::iter::Iterator<Item=_>`
|
= help: the trait `std::iter::FromIterator<_>` is not implemented for `i32`
I tried changing it to collect::<Vec<i32>>[0]
. But still getting the compile error. Let me know how can I change the code to make it compile.
Upvotes: 1
Views: 2244
Reputation: 10434
In the statement [(vec[0],vec[1],vec[2])].iter()
, you create a (fixed length) array with one element, then immediately iterate over it. The only iterator method you use is map
, so it would probably be much more idiomatic to simply have
let year = vec[0];
let month = vec[1];
let day = vec[2];
and then continue from there without using map
. Better yet, you can use this as an opportunity to parse day
as an integer, rather than having to parse on every branch of the match statements. This will also fix the issue you have that the type that day
parses into can't be inferred. You'd have to write day.parse::<i32>()
every time you parse it, so it's much easier to just to that once at the top. You may as well also parse year
, since you're going to need to do that below anyway. After these changes, you won't want to parse day
and year
again, so remove all the .parse().unwrap()
statements on them.
let year: i32 = vec[0].parse().unwrap();
let month = vec[1];
let day: i32 = vec[2].parse().unwrap();
After that, the changes the compiler suggests should be enough to get this to work. You no longer need to match month
as a &&str
(e.g. &"01"
) since you're not creating an array of &str
and iterating over references into it (in your original code, it would have been better to use into_iter()
instead of iter()
to avoid that). Additionally, the compiler will tell you that the match on month
isn't exhaustive. You'll need to add a catch-all branch in case the input doesn't match any of the other branches. I'd suggest something like _ => panic!("Invalid month")
at the end of the match statements.
Just some extra tips to get your code looking a lot better. The command cargo fmt
(this tool also exists under "TOOLS" on the playground) will automatically format your code to a more idiomatic style. This just makes it easier to read in general. I'd also recommend running cargo clippy
(also available on the playground) to catch any possible errors and get your code even more idiomatic. In this case, clippy makes a few small suggestions.
Just a general coding tip, I'd also separate this function into a function that parses the date, and one that finds the number of the day in the year. That way you don't need to do both at once and it's easier to think about. (This doesn't quite fit the format that the challenge gives you, so you'd have to call this parse function from inside the day_of_year
function).
I understand that the challenge asks you to return i32
, but since this is a computation that might fail, it would be far better to return an Option<i32>
(or better yet, a Result<i32, Error>
, where Error
is some description of the possible ways this can go wrong). This would work best if you also follow the suggestion above to separate this into two functions. Then you can parse and validate the date in the first function, and then given a valid date, calculate the day of the year. This would let you remove all the unwrap
calls (and the explicit panic I suggested). Knowing your function won't panic is a nice feeling.
Finally, there's a lot of repetition in this code, so you might be able to factor things out and not repeat yourself so much. For example, instead of having two match statements depending on whether it's a leap year or not, just have a single match and add one if it's a leap year. You might also want to have the number of days in each month in an array like [31, 28, 31, 30, ...]
. Then you can just use the month number and add up the appropriate number of days.
(also, a very small quibble: the problem specifies that it uses the Gregorian calendar, which doesn't have special cases for leap years at multiples of 3,200 or 172,800)
Upvotes: 0
Reputation: 2119
You don't need to iterate over tuple and call collect
at all. It creates a collection but your goal is just one i32
value. There is the fixed code: Playground
I also parsed values in advance and added _
branch in matche
es because it should be exhaustive. Ideally, you don't need match
either.
Update: a shorter version of the same code: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=a9a344c64f42332eb26f2a68fa260f72
Upvotes: 1