Help With Function to find MAX value in Array

I am new to Rust and trying to make a function to return the max value of an array and reverse an array:

The following code works but is not a function:

fn main() {
    let arr: [i64; 5] = [1, 2, 3, 4, 5];
    let mut large: i64 = 0;
    let mut i: usize = 0;

    large = arr[0];
    while i < arr.len() {
        if large < arr[i] {
            large = arr[i]
        }
        i = i + 1;
    }
    println!("Largest element in the array: {}", large);
}

Can a function be made and simply called by main?

fn max_number(_numbers: &[i64]) -> i64 {
    …
}

Best, David

FYI, code needs to be formatted by enclosing it between two lines of three (or more) backticks, like so:

```
Code goes here
```

You appear to have formatted your code by preceding lines with >, which is for formatting blockquotes.


On the matter of your actual question, exactly what trouble are you having with trying to convert your code into a function? Have you made any attempts, and, if so, what errors did the compiler give?

1 Like

I assume this is for learning purposes. I'll walk through how this can be done and some improvements that could be made.


Here's a first pass at modifying your program. I made minimal changes:

  • Added the function you suggested
  • Put most of your code in the function
  • Renamed arr to numbers in the function
  • Added large at the end of the function to return it
  • Left the array and println in main, and called the function to get large

There's still a warning about an unused initialization that we can get rid of:

-    let mut large: i64 = 0;
     let mut i: usize = 0;
-
-    large = numbers[0];
+    let mut large = numbers[0];

But here's a question for you: What should happen when someone passes in an empty slice? Indexing element 0 will panic if the slice is empty in this version. We'll come back to do this later.


Next pass: It's much more idiomatic to use iterators in Rust than to do "manual" indexing like you're doing here. for loops work with iterators, or more technically things that implement IntoIterator (that can be turned into iterators). Shared slices like &[i64] implement that trait, and the resulting iterator is over references to the contents (i.e. the iterator items are &i64).

So we can rewrite your while loop into a for loop and get rid of i:

    for number in numbers {
        if large < *number {
            large = *number
        }
    }

At this point there's a number of minor tweaks that could be applied, for example to get rid of the need to dereference number all the time. But now I want to return to the question about empty slices. We can address that by returning an Option instead of an i64, and then the caller can decide what they want to do (use a default value, panic, etc.)

fn max_number(numbers: &[i64]) -> Option<i64> {
    let mut large = numbers.get(0)?;
    for number in numbers {
        if large < number {
            large = number
        }
    }
    
    Some(*large)
}

Here,

  • I changed the signature to return Option
  • get is similar to indexing, but returns an Option to a reference of the contents
    • If the slice is empty, .get(0) will return None instead of panicking
  • The ? will return from the function if get returned None; otherwise, it evaluates to the contained reference, which gets assigned to large
  • Because large is a reference now, I removed all the dereferenes from number
  • I wrapped large in Some when returning it, and added a dereference to large

The biggest thing to learn here is probably the ? operator. It works with Result as well as Option, and is covered in this part of the book.

We need one more change to make this compile:

 fn main() {
     let arr: [i64; 5] = [1, 2, 3, 4, 5];
-    let large = max_number(&arr);
+    let large = max_number(&arr).unwrap();
     println!("Largest element in the array: {}", large);
 }

It's okay to unwrap here -- we'll only get None if the slice we pass in is empty, and we know that's not the case because we're passing in a reference to an array of size 5.

Here's the playground with those changes.

I think that's a good enough stopping point for this exercise.


Incidentally, finding the maximum element is a common enough operation that there's a generic way to do it in the standard library. It's a method on the Iterator trait. So you could just do this:

    // `large` is now a reference in this version...
    let large = arr.iter().max().unwrap();
    // ...or in this version it's still an `i64`
    let large = arr.iter().copied().max().unwrap();

(But I've assumed that's not the point of this post.)


P.s. See the pinned post on code formatting for this forum.

1 Like

quinedot,

Thank you for this awesome presentation which is really a complete lesson which I've saved for review,
appreciate the time and care + sharing.

Best,
David

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.