New rust user here, figuring things out, would appreciate some help, thanks in advance!
Brief context: I'm working on Day 3 - Advent of Code 2016 which requires reading numbers from a file. My code does this:
let contents = fs::read_to_string("3.txt").expect("File not found");
let mut valid = 0;
for line in contents.split("\n") {
if let [a, b, c] = &line
.trim()
.split_whitespace()
.map(|x| x.parse::<i32>().expect("Unparsable number"))
.collect::<Vec<i32>>()[..]
{
if a + b > *c && b + c > *a && c + a > *b {
valid += 1;
}
}
}
3.txt looks like
785 516 744
272 511 358
801 791 693
572 150 74
What I'm confused by is the line:
if a + b > *c && b + c > *a && c + a > *b {
If I add or remove prefix * on the addends, it doesn't seem to matter, but if I leave out the * before the right hand side of the >, I get:
calebegg@penguin:~/adventofcode/2016$ cargo run --bin 3-1
Compiling adventofcode-2016 v0.1.0 (/home/calebegg/adventofcode/2016)
error[E0308]: mismatched types
--> src/bin/3-1.rs:14:18
|
14 | if a + b > c && b + c > *a && c + a > *b {
| ^ expected `i32`, found `&i32`
|
help: consider dereferencing the borrow
|
14 | if a + b > *c && b + c > *a && c + a > *b {
| +
For more information about this error, try `rustc --explain E0308`.
error: could not compile `adventofcode-2016` due to previous error
Can someone explain or link to some relevant documentation? Thanks! Also, if there's anything un-idiomatic in what I'm doing I'd love to hear it. I was a little surprised to need to collect to a vec to destructure.
Comparison operators in Rust often only work when both sides have the same type. (At least for numbers and references to numbers like i32 or &i32 that's the case.) Adding two numbers always produces an owned value. No matter if you
add &i32 to &i32 (a + b)
or i32 to i32 (*a + *b)
or even mixed should work (I think?)
the result always will be i32 on the left hand side of the >, so an i32 is necessary in the right hand side, too.
// Panics on unparsable numbers
// Returns None for 0, 1, 2, or more than 3 numbers
fn three_i32s(line: &str) -> Option<[i32; 3]> {
let mut iter = line
.split_whitespace()
.map(|x| x.parse::<i32>().expect("Unparsable number"));
let a = iter.next()?;
let b = iter.next()?;
let c = iter.next()?;
match iter.next() {
None => Some([a, b, c]),
_ => None,
}
}
There's no need for trim due to the split_whitespace. Then your loop becomes:
for line in contents.split("\n")
if let Some([a, b, c]) = three_i32s(line) {
if a + b > c && b + c > a && c + a > b {
valid += 1;
}
}
}
But the pattern for x in iter { if let Some(y) = expression(x) { /*...*/ } } can be more succinctly written with flat_map:
for [a, b, c] in contents.split("\n").flat_map(|line| three_i32s(line)) {
if a + b > c && b + c > a && c + a > b {
valid += 1;
}
}
And since all we're doing in the flat_map closure is calling a function with the same argument as the closure, this can in turn be shortened to:
for [a, b, c] in contents.split("\n").flat_map(three_i32s) {
if a + b > c && b + c > a && c + a > b {
valid += 1;
}
}