Nitpicking: assignment is an expression that returns () in Rust, meaning `foo(a = 1, a)` is valid if foo has a signature like `fn foo(x: (), y: i32)`. However, arguments are (mostly) defined to be evaluated from left to right.
On the other hand you can assign within expressions in Rust, and it's generally not been an issue that I've seen anyone complain about. However, that might be because the type checker usually catches you if you're making a mistake; assignment/variable binding return the type `()`, which doesn't work inside ifs or whiles
Since you're using Rust as an example there, worth noting that unlike in C the assignment operator in Rust does not evaluate to the assigned value (it evaluates to the unit value `()` instead). In combination with the fact that `if` expressions in Rust require their conditions to be bools (the language has no automatic coercion to bool), this means that `if foo = 0` is guaranteed to be a type error.
(This difference in the behavior of the assignment operator is a result of Rust's ownership semantics; since assignment transfers ownership, having the assignment operator evaluate to the assigned value would actually result in the original assignment being entirely undone!)
For example, in C the assignment operator evaluates to the value of the right-hand side expression (so `int x = (y = 2);` initializes `x` to 2). But in Rust, the assignment operator always evaluates to `()` (a.k.a. "unit", the empty tuple), and trust me, this is how you want the language to work in the presence of pervasive move semantics and single ownership. Furthermore, nothing in Rust is "truthy": if-expressions accept a boolean and nothing else (this is also, IMO, exactly what you want in every language, but I'm sure others will disagree :P ). So `if x = 2 {` is a compiler error in Rust because `()` can't be coerced to a boolean (to say nothing of the fact that this reassignment would probably be a compiler error anyway because most variables in Rust are immutable). This doesn't require any acrobatics, it's just the natural fallout of how the rest of the language works.
This mistake also requires that the assignment operator has a result (and further, that the result can be silently coerced into a boolean for whatever reason)
In both Rust and Swift, this mistake doesn't compile, their assignment operators don't have a result and so it can't very well be true (or false) ‡
‡ Technically the result of Rust's assignment operators is the empty tuple, which is the closest to "doesn't have a result" in the type system. I don't know about Swift.
And there are cases where shadowing is not acceptable e.g. when updating bindings within a loop, you usually want to see the updates from outside the loop.
reply