How to correctly return slice

Hi guys,
I'm new to rust and began to learn it by doing some examples from:
https://doc.rust-lang.org/book/ch04-03-slices.html

My confusion stems from the following code, specifically line (marked by me):

fn first_word(s: &String) -> &str {
    let bytes = s.as_bytes();

    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return &s[0..i];//HERE <<< why do I have to use return not just &s[0..i]
        }
    }

    &s[..]//HERE I actually don't use return and it compiles!!!
}

What's more, the code will simply not compile if I don't use return!!!
I always thought that in order to indicate return statement in rust one simply doesn't end line with semicolon. That's obviously wrong as the above example is showing.
Any ideas why and how to understand this behavior?

A function will return the last expression automatically. The last expression here &s[..].
If you want to return early, as in the loop, an explicit return is needed.

1 Like

If you remove the return in the if, then you are trying to assign the value &s[0..i] to the if-expression, which fails as if expressions can only take a value if they have an else-block. The return statement explicitly returns from the function now, instead of just assigning the value to the surrounding block.

See also this discussion.

2 Likes

fn return_int(arg: i64)->i64
{
if arg > 0
{
1
}
else
{
2
}
}

In the above code which is doing exactly what my OE but with ints I don't have to use return.

The ints are first assigned as the value to the if. Then, since the if is the last thing, the value of the if (which is one of the ints) becomes the value of the function.

1 Like

Thanks for the answer.
To me this kind of behavior is simply confusing. Why can't we simply not put the return? I mean, it makes the whole returning in rust rather peculiar. There are places where one doesn't use return, there are other places where one must use return. Mess.

Making if and match and friends be expressions is very useful, and it is only natural to extend it to functions.

2 Likes

Will it? Even if it is a line ended with semicolon?

I'm not denying it. I'm simply showing illogical/confusing way in rust of returning from function.

Line ended with semicolon is a statement, which is an expression evaluating to unit. So, yes, the function will return this last expression, i.e. ().

1 Like

It may be different from other languages, but once I learned it, I found it the most natural thing in the world.

1 Like
fn foo() {
  42;
}

This returns () as the ; turns it into a statement. (If I remember the specification correctly).

@s3bk
Sure but this is really, really confusing. By this I mean:
42;//returns () - kinda pointless, where function sig specifies that it doesn't return anything
42 //returns 42
return 42; //returns 42

Really, really un-intuitive.

It is actually very clear.
() is rusts way of saying nothing. (An empty tuple)

fn foo() -> () and fn foo() are identical. Omitting the return signature just means -> ().

And the last expression is returned. That is one rule and one definition.

1 Like

It is confusing that you sometimes have to specifically write return and in some (most) cases you don't.
Tbh, I've learned few languages in my life and never had an issue with the way we return from functions.

If it was very clear as you stated, I would not ask that question.

I'll try to describe this again. It is based on the following:

  • Last expression in the block (including function) is returned, unless the "return" keyword forces the function to return earlier.
  • Statements are expressions with value ().

Now:

  • What is not clear in these two rules?
  • What is not clear in how the current behavior derives from them?

You can still write return for the final expression if you prefer that.
Rust just gives you the option of omitting it.

2 Likes

It is clear but convoluted/surprising/confusing for newcomers. In rust in order to return from function sometimes you are forced to use return, sometimes you don't have to but you can. << This is confusing.

Maybe one part is that in Rust, there are often many ways of solving a particular problem. It is totally fine to ignore iterator adapters and start with for loops, use explicit return everywhere and always write the return signature.

1 Like