> No, it forces you to have a cover grammar for things that could be either a type or an expression, i.e. you need to be able to parse the superset of both possibilities, from when you see the '('. This isn't something Rust needs or has at the moment.

100% agree, but I think it's probably worth the extra complexity in Rust's implementation to restore familiarity with casting.

I don't know if this is already a concept somewhere (I feel like it has to be) but I think that given a software or information encoding problem, there's a certain base complexity. You might be able to solve that problem in multiple different ways, splitting up the complexity in each one, but the total amount of complexity is still there.

Memory management is a good example. Memory must be managed somehow, and in languages like Python, Java, and even Rust (with lifetimes and (A)Rc) the complexity of that management is in the language/platform implementation whereas in languages like C it's in the application. Regardless, it exists.

So I would prefer that this complexity be in the implementation in order to maintain familiarity with the long history of systems and applications languages. I recognize it's more work for Rust, but as a user of Rust and not a maintainer, I'm OK with that ;)

Re: Context managers, I think we agree here and in fact, basically all I want out of a context manager is Rust's blocks. I guess a block without a statement is a little strange, but actually in Rust it's kind of idiomatic so I can get behind it.

> Then you end up with a pile of ceremony and junk, with 'return's everywhere (some functions would get 50% larger/more noisy, just from the 7 characters "return "), and, as with most things, it makes macros and generating code more annoying.

Woof, I do _not_ consider `return` noisy. Along the same lines as "everything is an expression is weird", I think implicit returns are weird. I guess it's maybe like everything; like when you work in Python you think braces and semicolons are annoying noise, when you work in Java you think manual memory management is annoying noise, and now maybe that goes for `return` in Rust. I'm almost never irritated by "ceremony" -- I'm pretty good at typing. Instead it's the "neat tricks" and inconsistent structure of programs that really eats my time and burns my brain cycles.

I'll admit to not being a huge fan of macros -- especially in systems languages. I think inlining functions is far less surprising, and the fact that you have reduced power compared to macros means there's far less surprising behavior (i.e. "why am I returning early..."). I know they're good for getting rid of "ceremony" but I'm guessing we'll end up disagreeing about how important that is :) But consequently I'm not really willing to give up anything to make macro writing easier.

> However, just to be clear, these changes you're suggesting are making Rust less consistent. There are many places where Rust is different to C and C++, but it is also more consistent, which is one of your complaints. (You can see "everything is an expression" style of thinking has benefits even in C: the classic do ... while(0) trick for macros, plus GCC's statement expressions.)

Haha well, I'm not gonna defend do/while(0). Textual macros and optional braces are obviously (now) not a good idea.

Consistency is fine as long as it's good consistency. Sure C mixes a lot of statements with a few expressions, but that never bothered me because that's practically all mainstream languages. "Everything is an expression" is consistent, sure, but at what cost?

> There's no other way to conditionally deconstruct an enum down into its parts, other than 'if let' and 'while let' but those have the problems of if/else.

I guess really what I want is for there to be a construct that switches between different enums, and there to be a different construct that switches between different values. Conflating the type with the value is confusing to me. Ex:

fn inspect(thing: ThingEnum) {
match thing {
case ThingEnum::ThingOne {
switch thing.thing_one_field {
case 1 {
// do ThingOne.thing_one_field == 1
}
}
}
// etc.
}
}

Yeah it's a little pyramid-y, but hey welcome to matching and variant types. If you really worked things around you wouldn't need to nest so far, but that's probably too much of a syntax change:

fn inspect(thing: ThingEnum) {
thing=>variant(ThingEnum::ThingOne) {
switch thing.thing_one_field {
case 1 {
// do ThingOne.thing_one_field == 1
}
}
}
// etc.
}

Anyway there are a lot of benefits. The distinction between switching on type and value is very clear. Blocks and control flow are very clear. It uses previously standard constructs (switch/case). I really don't need to know anything about Rust to know how this works; it is self-evident. Really maybe the only ambiguous thing is "match", which should probably be like "variant" or something, but whatever.

> In any case, my experience is almost all matches are simple pattern matching, there's no guards, no @s. It's theoretically possible (and occasionally occurs, sure) that someone writes a ridiculous match using its 3 separate constructs, but that's true of many things? I personally find the most annoying thing is how deep the code of a match ends up being indented.

100% agree.

> That's not the impossible/difficult cases: nested matches are:

let foo: Option<AnEnum> = ...;
match foo {
Some(inner @ AnEnum::Variant(_, _ , _)) => { ... }
_ => { ... }
}

While I get what this does, it looks very noisy. Losing the ability to do this in a single construct is fine w/ me if this is the result.

> ...C++ style of a namespace splattered across hundreds of headers.

That's a fair point and worth worrying about, haha. Good call.

> So a sigil for Box/owned pointer is okay, but not for any other library-defined types?

Mostly I just didn't think the (A)Rc pointers needed a sigil, and I thought it was weird that what most people would think of as a pointer used to use `~` and now uses `Box`, whereas the pointer you would (mostly) never use is the one with the most familiar sigil (`* `). Feels like that one should be `Raw` and `Box` should be `* `. Library-defined or otherwise doesn't really matter to me; and if you're building something that can't allocate, the compiler will just tell you when you can't use `alloc`/`new` or whatever. EZ.

I get what you're saying about unsafe code being closer to C though. I guess I would have made regular Rust closer to C and had unsafe be less ergonomic, as an interesting way to discourage people from using it (see Python's prolific use of `__` everywhere), so that's probably the root of our disagreement here.

> While it's fair that .. is a little impenetrable, what's the alternative?

I would get rid of struct destructuring entirely. It's only real use is inside of match, and that's packing more things into match.

> I think a lot of these sort of "Rust isn't consistent/is too complicated" discussions come down to familiarity

Oh I'll definitely cop to being 1000x better at C than I am at Rust, and the more I use it the more I'm fine with it. But moving from C to Rust (or any language to Rust) is so hard because of all of these things. I mostly work in C, Python, Java, and JavaScript and Rust is very different from all of those -- and it's hard for me to justify those differences. And as a C programmer, the borrow checker isn't responsible for the learning curve. Rather, it's all the "neat" things in Rust. All I really wanted was C with a borrow checker, or Java without GC and a 90s idea of OO (hand waving a lot here). I honestly don't see why we had to tack on all this extra stuff.

> I also think it's worth separating out "Rust is complicated" and "Rust is different", although the consequence of the two probably end up being similar in a lot of cases (hard to build an accurate mental model because things are unexpected).

Definitely. Point taken :)