I have gone through a handful of posts on this topic, in Stackoverflow. I am confirming if I have got the essence, correct.
I want to find out the maximum value in a Vector, using Iterator. Because of the way, iterators are implemented in Rust (discussed here, and here), I understand that I will always receive a reference to the element (if it exists) and not its value.
This doesn't compile.
pub struct WorkCard {
pub worker_id: u32,
pub largest_sack_moved_by_worker: u128,
pub sacks_moved: Vec<u128> // collection of duration of each call to API
}
impl WorkCard {
pub fn new(worker_id: u32, sacks_moved: Vec<u128>) -> WorkCard {
let mx = sacks_moved.iter().max().unwrap_or(0);
WorkCard { worker_id, largest_sack_moved_by_worker: mx, sacks_moved }
}
}
fn main() {
let sacks: Vec<u128> = vec![5,2,9,8,1];
let _worker1 = WorkCard::new(1 /* worker_id */, sacks /* sacks_moved */);
println("Worker {}, moved even a sack of {} KG!",
_worker1.worker_id,
_worker1.largest_sack_moved_by_worker
);
}
The compiler clearly indicates what the problem is:
Compiling playground v0.0.1 (/playground)
error[E0308]: mismatched types
--> src/main.rs:9:53
|
9 | let mx = sacks_moved.iter().max().unwrap_or(0);
| --------- ^
| | |
| | expected `&u128`, found integer
| | help: consider borrowing here: `&0`
| arguments to this method are incorrect
|
help: the return type of this call is `{integer}` due to the type of the argument passed
--> src/main.rs:9:18
|
9 | let mx = sacks_moved.iter().max().unwrap_or(0);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-^
| |
| this argument influences the return type of `unwrap_or`
Of course, this works!
impl WorkCard {
pub fn new(worker_id: u32, sacks_moved: Vec<u128>) -> WorkCard {
let mx = sacks_moved.iter().max().unwrap_or(&(0 as u128)); // <-- forcing a numeric constant's reference
WorkCard { worker_id, largest_sack_moved_by_worker: *mx, sacks_moved } // <-- then, dereferencing !
}
}
My question is if I should take it that this is an idiomatic way to grab the maximum (or minimum) value off an iterator. If there is a more pleasant way, I would like to learn that.
NB: Thanks for everybody who has taken time to comment and guide me.
I think I should point out why I had been looking for an idiomatic use for such a common construct.I had been in serious 'C' world for the first decade and a half of my career, and there - as you all know - dereferencing using *-operator
was (and is) a part of daily ritual. However, as I learn Rust, I realize that using *-operator
to get a value is not really the norm. In fact, safer and cleaner operators/techniques/idioms exist to bypass that completely, in a overwhelming number of Use-Cases. Thus, was my question, if such a use (use of '*' operator) was OK, with the Rust experts. I had got my job done, yet I inquired because my own solution didn't seem right to me. :-P
I am very aware of the power and the inherent danger of using '*' operator. So, I am cautious.
Because of the way, iterators are implemented in Rust (discussed here, and here), I understand that I will always receive a reference to the element (if it exists) and not its value.
That is not quite true, you receive a reference not because of the way "iterators are implemented in Rust" but because you're using a borrowing iterator (slice::Iter
, as returned by slice::iter
).
Of course, this works!
It's not clear to me why you bother with (0 as u128)
, &0
would work fine. Even if it did not, 0u128
will provide a concretely-typed constant.
My question is if I should take it that this is an idiomatic way to grab the maximum (or minimum) value off an iterator. If there is a more pleasant way, I would like to learn that.
Pleasantness is in the eye of the beholder, defaulting to a reference looks fine to me, but you could use an adapter to convert the reference to an owned value, either Iterator::copied
:
let mx = sacks_moved.iter().copied().max().unwrap_or(0);
or Option::copied
:
let mx = sacks_moved.iter().max().copied().unwrap_or(0);