if without else

29 views
Skip to first unread message

Michael Snoyman

unread,
Jul 1, 2021, 3:10:06 AM7/1/21
to Begin Rust
One of our users on Educative asked a question about the "Hello If" section of the book, so I thought I'd write the clarification to the whole list. Then I decided to put the response on my personal blog as well. You can read it online, which will have nicer syntax highlighting, at:


I'm also including the content below:

* * *

In the book, we say:

> However, there is one restriction in the kind of if expressions we’ve seen so far. They must evaluate to unit ()

The user asked whether this was related to the fact that the main function is returning unit, which in fact is implied by the error message the compiler gives. However, that's _not_ the case. In the context of that section, "the kind of if expressions" we're referring to are if expressions without else blocks. Let's demonstrate. Consider this program:

fn return_number(is_even: bool) -> u32 {
    if is_even {
        42
    } else {
        43
    }
}

fn main() {
    println!("{}", return_number(true));
    println!("{}", return_number(false));
}

We have an if/else expression which evaluates to a u32. That becomes the return value of the function return_number. Everything is fine.

However, let's tweak this ever so slightly and remove the else:

fn return_number(is_even: bool) -> u32 {
    if is_even {
        42
    }
}

This no longer compiles, and instead gives the error message:

error[E0317]: `if` may be missing an `else` clause

We can try to "fix" this by ignoring the result of evaluating the if expression, like so:

fn return_number(is_even: bool) -> u32 {
    if is_even {
        42
    }; // semicolon ignores the result!
    43
}

Even so, the compiler is not happy, and gives the same error message above.

Takeaways:

* If you have an if without an else, the body must _always_ evaluate to unit, no exceptions.
* If you have an if and an else, their blocks must evaluate to the same type.
* If you return the result of an if/else expression from a function, the return type of the function must match the type of the expressions in the if and else blocks.

Michael Snoyman

unread,
Jul 2, 2021, 9:41:54 AM7/2/21
to Alejandro Buffery, Begin Rust
While you're right that `&5` is technically an address, it's not particularly common to think about things that way in Rust. It's better to think of it as a reference to an `i32`, and not worry about its in-memory representation. At least in my opinion.

That said, even thinking about its representation, we can walk through why equality works as we'd like. That's because of how we define the equality operator on references. You see, in Rust, equality is overloaded via a trait, PartialEq. And the definition for equality on a reference says that it should first dereference to get the underlying value, and then compare that underlying value. You can see that in the definition, which is admittedly a little more complex than the code we include in the book, but hopefully readable:


    #[stable(feature = "rust1", since = "1.0.0")]
    impl<A: ?Sized, B: ?Sized> PartialEq<&B> for &A
    where
        A: PartialEq<B>,
    {
        #[inline]
        fn eq(&self, other: &&B) -> bool {
            PartialEq::eq(*self, *other)
        }
        #[inline]
        fn ne(&self, other: &&B) -> bool {
            PartialEq::ne(*self, *other)
        }
    }

I hope that helps.

On Fri, Jul 2, 2021, at 4:07 PM, Alejandro Buffery wrote:

Michael, may I ask you a question regarding Exercise 1 References (see below).

Exercise 1 has two possible answers *x==5 and x==&5.

I do understand the logic behind why these are the correct answer to the exercise, what I do not fully grasp is how all works in the case of x==&5.

I will try to explain my reasoning:
  • I believe that &5 is the memory address - for example 0x7fff9575c05f - of where a value 5 is stored
  • when passing &5 to the function is_five, x is assigned the value of that memory address, so now we have x=0x7fff9575c05f.
  • if x is &5, then if we substitute x for &5 in the expression x==&5, then we get &5==&5, which seem should give as result 'true'
  • what I do not fully grasp is why the address of each &5 in the expression &5==&5 is the same, are not each 5 like two independent instances of a value 5 with their own unique address, so each address is different and therefore &5==&5 should be false.

What am I missing out or got mixed up ?


P.D. I have tried to post my question on the platform and not found my way around on how to do it, it is not intuitive enough for me


============================================================================================

Exercise 1 #

The is_five function below has a bug. You can fix it by either adding in a borrow (&) or deref (*). Try fixing it both ways.



fn is_five(x: &i32) -> bool { x == 5 } fn main() { assert!(is_five(&5)); assert!(!is_five(&6)); println!("Success!"); }




El jue, 1 jul 2021 a las 15:29, Michael Snoyman (<mic...@snoyman.com>) escribió:

My pleasure!

On Thu, Jul 1, 2021, at 2:58 PM, Alejandro Buffery wrote:
Thanks Michael for such a prompt and clear exemplification !
Reply all
Reply to author
Forward
0 new messages