It's also worth noting that hiding a function call behind .field syntax isn't great, because a field name is a place, and &obj.field borrows from obj, but &obj.func() borrows a temporary.
(The cost argument isn't particularly salient due to Deref coercion already existing and hiding arbitrary computation.)
I think trait implementations could afford sugaring out the type annotations entirely (for the arguments, too) because the SSoT for the types is the trait definition.
I would be unhappy to see this feature added to Rust, with or without the = to make parsing easier.
I don't think the boilerplate in writing simple functions is really that bad: okay so for simple constructors you repeat the Self type and braces twice. Certainly this doesn't make it difficult to read. If writing it is really so onerous, I believe rust-analyzer will already autocomplete it for you.
Rust does not need more valid syntaxes for people to have to learn about and decide which to use in which context. When we introduced this in the past in more minor ways (like having two file paths a module can be at), it has caused a fair amount of uncertainty and confusion. The only time this kind of thing works well in my view is when it is very obvious when it is an advantage to use it. For example, the field name ellision to replace Foo { bar: bar } with Foo { bar } works out well because if you can use it, there's not a strong debate. This syntax, on the other hand, permits usage in tons of cases that will be debatable whether or not its an improvement on the ordinary syntax.
I'd also be concerned about a feature which infers the return type of a function from its body in a way that API users can rely on. Rust right now has a consistent rule that the function signature is the source of truth and the body of the function is checked against that. Switching directionality so that the signature is inferred from the body can cause the type system to catch fewer errors (because there can be some valid typing for this function, even if it isn't the one the user thought it was!).
I'd rather see whatever syntactic complexity budget Rust has left (and it's definitely not much) on syntactic sugar aimed at making things that are actually hard to write easier. The obvious example is generators, which makes it much easier to write an iterator than hand implementing the trait.
Every syntax is debatable, in the sense that for every possible alternative way to write things, people will debate which one is better. (Field init shorthand by ranweiler · Pull Request #1682 · rust-lang/rfcs · GitHub had plenty of opposition when it was proposed.) The interesting question is how many people will prefer one over the other. In an ideal world, we'd provide an alternative syntax that, when it's possible to use, is considered generally preferable, along with an optional lint that converts if possible.
I would not want to have any non-trivial inference here. The rule applied should be that the type must be immediately obvious, textually, from the body. e.g. Self(...), Some(Self(...)).
Also, note that we already allow type inference of closure return values, which has generally worked out well, even though it sometimes ends up with a signature other than what the developer expects. I am suggesting, here, a more restrictive inference rule than the one for closures, which does not permit using any non-local information (information from outside the function body) to determine the inferred type.
I just don't see enough motivation here to justify the additional complexity. What's the goal beyond, "Have a different way to right a function body"? Because that alone, doesn't carry water IMHO.
Of course: all opinions are held. I would contend there's a clear continuum of how valid the disagreement is, and introducing new alternatives to a mature language that raise a high degree of debate would not be prudent.
This is difficult to engage with because "immediately obvious, textually" has no formal definition. All type inference is ultimately derived from the textual body of the expression being type checked.
Some of the examples you've given already don't strike me as more immediately obvious than any other type inference. In your gen example, you derive the Item type of the generator from whatever yield expressions are in the body. I don't see how that is restricted to be more immediate.
I know you like the idea of introducing this and using it for gen and async because it seems more parsimonious to you than the async fn syntax that we already stabilized, but the whole value proposition of async fn and gen fn is that you don't specify the type of the future/generator but still specify the inner return/yield type.
Closures are expression position elements, not item position. The way Rust is designed now, expressions are checked against the types specified by the items that scaffold those epxressions. This is the distinction that matters.
If that were the standard we operated by, many of the changes we've made over the years would never have happened. Fortunately, it isn't. The volume (in either sense of the word) of debate is never the determining factor; the correctness is.
Or, to put that another way: a discussion on par with the one for ? or .await should not stop us from making a syntax change, if and only if the change provides sufficient value. It's absolutely debatable whether any given proposal does, but the existence of debate doesn't by itself determine that.
No, it isn't. Some type inference comes from non-local information elsewhere in the program (e.g. let v = Vec::new(); will get the element type of the Vec from a later statement). The distinction I'm drawing is that 1) expression-typed function return types can only come from a named type appearing in that expression (and not, for instance, the return value of some other function), and 2) it cannot rely on anything elsewhere in the program. This is unlike, for instance, Haskell's type inference for functions, which can do more global type inference, which we don't want to add to Rust.
Also, to clarify something about the handwaving gen examples: I was attempting to write a trivial example in which the gen block contained a single yield that contained a value with a named type. For anything more than that (and perhaps even for that, to avoid too many special cases), I'd propose something like fn func() = gen<u64> { ... }, which makes the type immediately evident.
(I'd like to avoid opening the whole syntax question of gen/async/try/etc in this thread, as it isn't fundamentally tied to expression-valued functions or vice versa. One syntax debate at a time is quite sufficient.)
Again something a bit more formal than "e.g. this is an example on one side of the line or another" would be helpful.
What I get from your example here is that you wouldn't consider anything which has an unbound unfiication variable (i.e. the ?0 in Vec<?0>) to qualify. But a function body cannot have an unbound unification variable in it after its type-checked under any circumstance, because a function body must be fully typed on its own. Any unbound unification variables would result in a type error, so this example is completely irrelevant to this feature.
On the other hand, the examples you say do count all depend on some sort of information outside of the expression itself, so when you say it can't require "non-local information" I'm not sure what you actually mean. To be explicit, I mean that Self has to be resolved by knowing the receiver type of the impl block this function is contained in and Some is just a function imported from the prelude, so the signature of it needs to look up normal name resolution. You say here that the type of the expression cannot rely on "the return value of some other function," but you posted an example for which that is the case.
This isn't what I mean by debate: I don't mean debate about whether the feature is good or not, I mean that, if the feature exists, there are situations in which it is unclear whether using the feature is better than not using it. In this case, I am imagining that there would be expressions that are technically permitted by this feature but are debatably "too complex" for using this feature to be a good idea. That is the downside of this feature I am trying to articulate in this point, not just saying "don't do things that are controversial."
For example, two decisions we made in the module changes (neither of which were especially controversial compared to other changes) in my opinion in retrospect were maybe ill-advised: allowing both foo/bar.rs and foo/bar/mod.rs and allowing tree paths use foo::{bar::Bar, baz::Baz};. These added multiple ways to do something in a way in which it's not clear to users what the preferred way is, and they each have pros and cons. My experience as a user has been that this has resulted in confusion and discord within teams that is just a waste of time.
In contrast, eliminating extern crate from that same set of changes has been basically a pure win because its obvious what the best thing to do now is (don't write extern crate) even though it was far more controversial at the time. I think new syntactic features should have a similar obviousness about how and when to use them.
Moreover, the reason why there are expression-bodied members in C# is that programs in that language are quite verbose. One has to type the return keyword each time a value has to be returned from a function while in Rust any last statement without a semicolon means return.
I would also point that Rust has much stricter formatting rules than C# by default, so the problem with expression-bodied members is that the expression can be put on the same line as the method declaration or on the next line. The same thing with the arrow, it can be put anywhere. That really hurts readability as the body part isn't fixed and can jump from codebase to codebase, or even in the same repository.