I can't get the return value to work: Rustlings exercise if1.rs

Hi all, my first post and I'm a beginner so hope this is the appropriate place to post.

I'm doing the rustlings exercise if1.rs which asks to complete a provided function to return the "bigger" of two i32 values. I can't find a way to cover the a==b case.

Here's the code (read the comment I inserted to help clarify).

// if1.rs

// I AM NOT DONE

pub fn bigger(a: i32, b: i32) -> i32 {
    // Complete this function to return the bigger number!
    // Do not use:
    // - another function call
    // - additional variables
    // Execute `rustlings hint if1` for hints

    // MY COMMENT: The "if a > b { return a } b" code below is inserted by me.
    // All the rest is the rustlings-provided code.
    // My code works as far as compiling and not returning a smaller value
    //  but isn't technically a correct solution. If a == b it returns b
    // but really should return some indication that there was no
    // "bigger" number. I tried if{}else if{} to return a>b or b>a but when a==b
    // case falls through how would I return some indicator or error message?
    // As provided the function can only return an i32.

    if a > b {
        return a
    }
    b
}

// Don't mind this for now :)
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn ten_is_bigger_than_eight() {
        assert_eq!(10, bigger(10, 8));
    }

    #[test]
    fn fortytwo_is_bigger_than_thirtytwo() {
        assert_eq!(42, bigger(32, 42));
    }
}

If calling bigger(…) with two equal values is supposed to be an error, there’s different ways to indicate such an error. If you’re never supposed to call it like that, then panicking in that case is an option.

if a > b {
    return a;
} else if a < b {
    return b;
} else {
    panic!("the values were the same!");
}

Panicking would abort the whole program, and print an error message. For less extreme measures of error handling (i.e. not aborting the program), the type of the function would need to be changed. As you noted, as-is, the function can only return an i32. Failure can be represented by Option<i32> or Result<i32, SomeErrorType…> return types. E.g.

pub fn bigger(a: i32, b: i32) -> Result<i32, &'static str> {
    if a > b {
        return Ok(a);
    } else if a < b {
        return Ok(b);
    } else {
        return Err("the values were the same!");
    }
}

Feel free to take a look at the “error handling” chapter in the book for more information (the book is a useful learning resource in general, too).


By the way, note that in this exercise, you probably don’t actually need to identify an error condition. “Returning the bigger number” in case two equal numbers are given, can also be interpreted simply return either one of the numbers. (The choice as to which on doesn’t matter as they’re equal.) It might be a bit weird language-wise to speak of the bigger number (definite article) when you have two numbers and both are supposed to be “the bigger one”, but, eh... so be it.


Note that it’s more idiomatic not to use explicit “return” keyword (unless you want to return early). Instead, use the fact that Rust functions always return the final expression, and control-flow structures like if are expressions. So the second code example above would become

pub fn bigger(a: i32, b: i32) -> Result<i32, &'static str> {
    if a > b {
        Ok(a)
    } else if a < b {
        Ok(b)
    } else {
        Err("the values were the same!")
    }
}

If you want to do three different things depending on whether some value is larger, equal, or smaller, compared to another value, you can also use the .cmp method, and a match statement:

use std::cmp::Ordering;

pub fn bigger(a: i32, b: i32) -> Result<i32, &'static str> {
    match a.cmp(&b) {
        Ordering::Less => Ok(b),
        Ordering::Greater => Ok(a),
        Ordering::Equal => Err("the values were the same!"),
    }
}

See the “functions” chapter in the book for more information.

4 Likes

Absolutely.

In my opinion the last sentence is the key part -- the exercise didn't provide a way to signal the "equal" case, and didn't specify what you're supposed to do. Probably the answer is "it doesn't matter", and your code is fine as-is, so far as the exercise goes.

But let's look at a couple ways to be strict about bigger always returning a value which is definitely larger. Without changing the signature bigger, as you noted, there is no correct answer -- so perhaps you can just bail out altogether, and not return a value. Here are a couple of ways to do that:

// Explicit panic on the problem case
fn bigger_or_bust_v1(a: i32, b: i32) -> i32 {
    if a > b {
        a
    } else if b > a {
        b
    } else {
        panic!("a == b")
    }
}

// Run-time assertion.  After the assertion, we know the values
// are not equal.
fn bigger_or_bust_v2(a: i32, b: i32) -> i32 {
    assert_ne!(a, b, "The numbers must not be equal");
    if a > b {
        a
    } else {
        b
    }
}

These are also probably fine for the exercise. (Sometimes you will find an exercise or online problem that relies on things that they did not specify though; in this case maybe they meant for you to return either value when they are equal, as your code currently does.)

However, it's often much better to be able to handle unexpected situations in code, instead of just terminating the program. As you get further in the book/lessons, you'll cover the Result type. It is designed to let you make a function fallible (return an explicit "successful" or "error" value).

To do so, you need to modify the function signature, so this is going beyond what the exercise wants you to do at this point. But let's take a look anyway.

Below, I return either a successful result with the bigger i32 value (e.g. Ok(a)), or an error result that is a literal str (Err("...")).

fn bigger_or_err(a: i32, b: i32) -> Result<i32, &'static str> {
    if a > b {
        Ok(a)
    } else if b > a {
        Ok(b)
    } else {
        Err("The numbers are equal")
    }
}

The book covers both panic! and Result in chapter 9. Again, it's not needed for this particular exercise, but hopefully that gives you an idea of what a fallible function looks like in Rust. It's good that you're thinking of it already.

Here's a playground with the example functions at the bottom.

3 Likes

Wow, these were all such great responses. Thanks! I learned a lot.

1 Like

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.