Please keep in mind that just because something isn't what you're used to does not mean that it's "poorly chosen". The developers of Rust had good reasons for the decision they made. It appears that you do not understand what those reasons were, but that does not mean that they were bad decisions.
I'm also coming from a C/C++ background, although I was exposed to Pascal first because C++ didn't exist yet (yes, I'm that old), and I disagree completely about having the return types put first being a good thing. I think that doing so is strictly worse than the Rust/Pascal/Kotlin/etc. way, and really has no clear advantages other than a lot of people learned that way first and are used to it. The fact that it is familiar does not mean that it is better.
One problem is that once templates get involved, the return type can be dependent on the arguments to the function,which can make for some strange results, such as having something that comes *first* be dependent on something that comes *later* in the source file.
template <class T>
decltype(arg) foo(const T& arg) { return arg; }
Putting return types first can also result in some strange scoping issues:
class Foo {
private:
using APrivateTypedef = int;
APrivateTypedef foo();
}
APrivateTypedef Foo::foo() { return 42; } // Oops! Can't reference APrivateTypedef here! It's internal to Foo!
Foo::APrivateTypedef Foo::foo() { return 42; } // This doesn't work either! APrivateTypedef is private to Foo, and can't be accessed in global file scope!
auto Foo::foo() -> APrivateTypedef { return 42; } // This works fine: By the time we parse the return type, we know we are in the scope of Foo.
This became enough of a problem that the C++ committee eventually added support in C++ for making the return type come later also, as shown in the third example above. Because of this, the following is valid (but relatively unknown) C++ as per the "Alternative Function Syntax" that was added in C++11:
auto func(int x) -> int { return x+1; }
You can read up on the reason this feature was added for more details. Anyway, I think C++ would have been a better language if it had been designed this way in the beginning (and hence not need the 'auto' in the above, since *all* functions would have postfix return types) and have all return types come last. However, it's too late now, since the vast majority of existing C++ code doesn't put the return type last.
Another problem is that C (and therefore C++)'s decision to put 'types first" in declarations meant that it is impossible for the parser to know, when parsing something that *might* be a declaration or might not, whether or not it is expecting to parse a type without having a set of "known types" ahead of time available during the parsing phase to help it disambiguate. This means, for instance, that it is impossible for a tool (say an IDE) to parse a single source file in isolation without having first parsed all header files it includes first, since types defined in those header files might change how to parse the source file, and what constitutes valid syntax in it! (Not just valid semantics, but valid syntax!)
Languages like Rust (and Pascal, for that matter, which did it first!) avoid both of these problems by putting the type last, not first, and putting a colon in front of the type so that once the parser sees the colon, it knows that the next thing that follows it should be parsed as a type and not some other type of language construct. This means that the parser can parse even the names of types it has never seen before, since it knows ahead of time that what it is parsing HAS to be a type. This also means that in an IDE, for instance, if you invoke completion (Ctrl-space, etc.) right after a colon, the IDE can know that it should only show you types as candidates for completion.
Additionally, putting the type later provides the opportunity to have it be omitted completely in cases where it can be inferred, should a user want to do so:
x : int = 42; // OK
x := 42; // shorter, using inferred type.
Yet another example is that putting the function name first makes it very easy to visually scan down a list of functions for the one you care about, whereas C++'s approach puts the function name in various positions depending on how long the return types are, making you have to hunt for it on each line.
Some more reading on this issue:
https://ancillary-proxy.atarimworker.io?url=https%3A%2F%2Fstackoverflow.com%2Fques...
https://ancillary-proxy.atarimworker.io?url=https%3A%2F%2Fmedium.com%2F%40elizarov%2Ft...
https://ancillary-proxy.atarimworker.io?url=https%3A%2F%2Fnews.ycombinator.com%2Fi...
As for 'int' vs. 'i32', I would point out that your 'int' is not necessarily my 'int', and that means that code that you wrote that runs fine on your machine might do very strange things when run on mine, with a potentially different architecture and hence a different size of int. So if you intend to write portable C++ code, you really should be using int32_t or int64_t or whatever to be specific about how big of an int you really need in order for your program to work correctly. Rust mandates this to ensure that code is portable, and also makes the type names shorter ('i32' is shorter than 'int32_t') to make doing so more pleasant.
I'm not as fond of the whole 'implicitly returning the last expression' thing, but it does make for some wonderfully concise code when lambdas get involved. The 'return' in C++ lambdas makes them rather large and unwieldy compared to those in other languages.