What is the actual reason of having `->` before return type

There is no real semantic reason. However, syntax does have value (do you see many people writing lisp :wink:¹?). I believe that the reason for using the arrow, as discussed above, is threefold:

  1. Mathematically, specifically in type theory, an arrow represents a mapping between types. All Rust functions (ignoring closures) are named, and their signatures define the type mapping. Using an arrow to create a signature that defines a type mapping is common in many other functional programming languages, including languages in the ML family which inspired Rust. Additionally, if we extent the type theory argument, we can see that types are explicitly annotated using value: Type. It doesn't make sense to use : to denote type signatures. In the narrow sense, : means 'is of type'. Although it is true that a function fn _(x: usize) -> String 'is of type' String, that's not the full story. To produce that string, a transformation has to occur, and a value of another type (in this case usize) is required.
  2. Lexically, the presence of an arrow is clearer than the absence of one. A function, say:
    fn count_vowels(string: &str) -> usize { ... }
    
    can be read as: 'the function count_vowels takes a string and produces an integer.' The arrow can be read as 'produces', 'becomes', or 'maps to', which makes sense when reading code literally.
  3. Visually, an arrow helps maintain consistency and shows the presence of functions. For example, A higher-order function might have the signature:
    fn map_vec<A, B>(f: fn(A) -> B, v: Vec<A>) -> Vec<B> { ... }
    
    This consistency shows that this function takes a function; the arrow stands out in the argument. If anything, we should be upset about the lack of arrow notation in closures :upside_down_face:!

In conclusion, you have to remember that Rust is more 'ML in C++'s clothing' than an exact analogue to a C-style language. Rust has been inspired by functional programming languages, which generally include an arrow to notate the mapping produced by a function.


¹: As someone who writes lisp on a regular basis, I have permission to make this joke.

7 Likes

Closures do use arrows if you bother specifying the return type. You can be totally explicit:

iter.map(|foo: Foo| -> Bar { foo.into() })
5 Likes

rustlang's documentation uses "Source Code Pro" font in which -> looks as an arrow. But if you use other monospace fonts like what this forum and github uses, it looks bad.

I don't know about any other monospace font in which -> look good.

math uses 'arrow' not - and > together

In haskell, you need to first declare and then implement. so it make sense to have and arrow like notation in type signature.

But in rust you have to declare and implement the function at the same time. If rust did it like what haskell does, that would make more sense to use an arrow notation.

some of you said that you like to have : before return type.

but i don't think that is a good think to do. That will increase confusion.

As i know that function don't have type, they take value and return value, a machine.

At least -> serves as a decorative purpose.

Unfortunately, most keyboards don't have an key*. I've look at popular non-QWERTY layouts, and none of them have dead keys for typing arrows, so while using Ø for the Never type would be feasible in QWERTZ T2 and Colemak, using arrows in the syntax isn't really an option.

* the cursor navigation keys are not relevant; we're talking about characters that can easily be typed.

3 Likes

what if rustfmt replace -> with an arrow

At that point just use a font that combines those two characters into something that looks like an arrow

4 Likes

This is a legitimate concern. However, I think it's a lot smaller of one then you're making it out to be. For instance, we can use google fonts to look at what -> looks like in the most used monospace fonts: Google Fonts

Of this, I'd say IBM Plex Mono, Cousine, Cutive and B612 look bad, but the remaining 17 all align the - and >, and look good. This is a limited sampling, but from this, I know that there are at least 17 popular monospace fonts where this looks "right."

Besides regular fonts though, there's another (possibly better) solution: configure your editor to use font ligatures. With ligatures, any -> in source code will always be displayed as a connected arrow. I think this should be very similar to your suggestion:

But with the advantage that -> is always displayed as an arrow, even when immediately typed, and that we don't have to actually change the source code.

I think using ligatures is also what @Nokel81 is suggesting?

2 Likes

Yes, I just couldn't remember the name of it.

I can easily type ‘→’ as AltGr+I.

Not sure why you’d want to use ‘Ø’ for the empty type; ‘∅’ would be more appropriate. Although the empty type is usually denoted by ‘⊥’ anyway.

1 Like

There is no standard key combination on a US Mac keyboard that generates an arrow. When I type the equivalent on my US Mac keyboard I get ≥I. Other keyboards for other languages will generate different combinations.

I started Rust before Go, and I found Go style harder to read. A friend started Go before Rust, and he finds Rust style harder to read…¯\_(ツ)_/¯

8 Likes

It's come up in passing many times. i don't know if there's ever been a dedicated discussion about it.

Personally I've never really been a fan. Even in C# -- where the language isn't expression-oriented by default, so using => saves a return -- I'm not a big fan of it in functions, because it's just another thing to argue about in formatting discussions. (I do like it in C# for get-only properties, though.) So in Rust when the difference between { 4 } and = 4; is just a space, I'm not convinced that it pulls its weight.

1 Like

This thread has completely derailed from the original question, and few people even seem interested in attempting to answer it at this point.

Yes, Rust has slightly different syntax than other languages. My question is simple: What is the purpose of asking? Like it or not, that is the syntax, and there's no conceivable way it will ever change.

Is this question just for historical knowledge? If so, I think this should be closed, as it seems clear that no one definitively knows (except graydon, likely). OP's history of creating threads isn't great either, but that's a separate issue.

9 Likes

This has been documented long time ago:

-> for function return type

This is to make the language easier to parse for humans, especially in the face of higher-order functions. fn foo<T>(f: fn(i32): i32, fn(T): U): U is not particularly easy to read.

https://doc.rust-lang.org/1.6.0/complement-design-faq.html#--for-function-return-type

12 Likes

When considering current rustfmt, the difference between { 4 } and = 4; are two lines and an extra indentation. The rightward drift is why "body as expression" was mentioned in RFC 2585. The alternative is to format such code with unsafe { on the same line of the function's {. Each solution has its own weirdness.

unsafe fn foo() -> Bar { unsafe { // <-- two `{` on same line
    body
}}                                // <-- two `}` on same line

// vs.

unsafe fn foo() -> Bar = unsafe {
    body
};                                // <-- note `;` at end of block

I kind of agree that discussing function-body-as-expression is off topic.

Since the question has been answered (thanks for tracking it down, @kornel!), this thread can be locked up.

2 Likes