tl;dr: ok, I already solved this while writing this post and before posting, but I've decided to still post it in the hopes that someone maybe, possibly, pretty please(?) would actually find this useful. Don't read this if you're not a beginner - to save your time.
In rustbook's generics chapter there is an example of extracting a function that finds the largest item(of i32 type there) in a vector. It eventually looks like this:
fn largest(list: &[i32]) -> i32 {
let mut largest = list[0];
for &item in list.iter() {
if item > largest {
largest = item;
}
}
largest
}
fn main() {
let number_list = vec![34, 50, 25, 100, 65];
let result = largest(&number_list);
println!("The largest number is {}", result);
let number_list = vec![102, 34, 6000, 89, 54, 2, 43, 8];
let result = largest(&number_list);
println!("The largest number is {}", result);
let number_list = vec![]; //FIXME: How handle this case?!
let result = largest(&number_list);
println!("The largest number is {}", result);
}
I notice their example does not handle empty vectors, probably because it's not relevant to the point they're trying to make in that context and thus are trying to keep things simple. But it would be really nice to mention that in the text.
For learning purposes I attempted to find a way to handle that case (when the vector is empty) and ran into some trouble.
The simple/easy way that works seems to be this(playground):
fn largest(list: &[i32]) -> Option<i32> {
if list.is_empty() {
return None;
}
let mut largest = list[0];
for &item in list.iter() {
if item > largest {
largest = item;;
}
}
Some(largest)
}
But I'm interested in the other ways too (for learning purposes!).
One way that I want to see if can work, is without using if
s and without returning early if the vector is empty, therefore I've the following non-working code(because vec::get(index)
returns an Option<&i32>
instead of Option<i32>
thus I have to somehow reference the local variable item
when setting largest
(??) ):
fn largest(list: &[i32]) -> Option<&i32> {
let mut largest:Option<&i32> = list.get(0);
for &item in list.iter() {
if item > *largest.unwrap() {
largest = Some(&item);
}
}
largest
}
error[E0515]: cannot return value referencing local variable 'item'
playground link
How could I make this work?
Since this is for learning purposes, I'd appreciate small hints instead of full solution unless it simply can't be done, then I need to know as such.
Oh, this is ridiculous: I just found the solution(playground):
fn largest(list: &[i32]) -> Option<&i32> {
let mut largest:Option<&i32> = list.get(0);
for item in list.iter() {
if item > largest.unwrap() {
largest = Some(item);
}
}
largest
}
It's weird because for item in list.iter()
used to not work before (in 2017) (on second thought, it worked just not without changing the rest of the code), and yet that link explains why:
in for item in list.iter()
, item
is actually an &i32
ie. a reference to an i32
in for &item in list.iter()
, item
is actually an i32
ie. an i32 (moved out from vec)
Ok, this is a bit unclear to me due to possibly implicity Copy trait being at work here, so I'm re-doing the above with a non-Copy type:
ok it worked (playground)
#[derive(Debug, PartialOrd, PartialEq)]
struct NonCopyI32(i32);
fn largest(list: &[NonCopyI32]) -> Option<&NonCopyI32> {
let mut largest:Option<&NonCopyI32> = list.get(0);
for item in list.iter() {
if item > largest.unwrap() {
largest = Some(item);
}
}
largest
}
And here follows the non-working variant that shows that in for &item in list.iter()
a NonCopyI32 element moves out of the vector (but if using just i32
instead, it would've just been copied, so non-obvious - to me) (playground):
#[derive(Debug, PartialOrd, PartialEq)]
struct NonCopyI32(i32);
fn largest(list: &[NonCopyI32]) -> Option<&NonCopyI32> {
let mut largest:Option<&NonCopyI32> = list.get(0);
for &item in list.iter() {
if item > *largest.unwrap() {
largest = Some(&item);
}
//let a:u32=item; //to see type of `item`
}
largest
}
(to see how the above errors if the i32
type(or equivalent) were used instead, then only add , Copy, Clone
to the derive line)
Well, this was fun!