@buherator Could you please elaborate what you'd rather see? imo, if-let and let-else are perfectly reasonable keyword combinations, combining a refutable match with a assignment. Without these constructs, assignments can only be irrefutable patterns.
if-let - run the block if the let assignment matches the refutable pattern
let-else - run the else block if the let assignment doesn't match the refutable pattern. the else block has to diverge (can only evaluate to the ! type aka "never" type)
refutable pattern: a pattern which might not match, such as the pattern Some(val) matching against a Option<u8>
irrefutable pattern: a pattern which always matches, like a simple name, or a tuple pattern matching against only that tuple (unpacking the tuple)
considering this, what keyword would you propose? how would you express "refutable pattern matching assignment" as a longer yet not overly verbose keyword?
also, the & just means "reference", and i haven't had that mess with searching for a error
@risottobias @buherator i feel your comment stems from a misunderstanding of what kind of language Rust is. You're approaching it eith a very statement-based mindset, but Rust is a expression language. It doesn't have ternaries, its normal "if" is an expression, meaning it can evalue to a value:
let mut num = 0;
if some_cond {
num = 5;
} else {
num = 10;
};
can be written as:
let num = if some_cond { 5 } else { 10 };
which is far more verbose and readable than JS/TS ternaries. This same way also works for match, loop, etc.
Your linked example may be somewhat dense, but its rather clear as soon as you understand that all { } blocks are expressions. i'm having a hard time reading the comparison to brainfuck as anything but bad-faith.
@buherator hmm, i think to clear up why the order is that way around it makes sense to elaborate a bit on a core part of the syntax which is very useful in some cases but can be ignored a lot of the time:
every assignment in rust is pattern matching:
let a = 5;
^ this is a irrefutable pattern
which means you can do this:
let Vec2(x, y) = some_vec2;
^^^^^^^^ this stores the first field of the Vec2 struct in x, and the second in y
its just that most assignments are, as i tried to explain, irrefutable patterns. they can only be used to unpack/destructure a specific type.
thats why the order of if let <pattern> = <value> { <block> }
is like that.
its analogous to for <pattern> in <iter> { <block> }
in for loops like this you also have the pattern first then the value, and then the block.
your example is a interesting proposal, but it adds a new syntax for assignment, whereas the current if-let and let-else syntax keeps the same syntax for assignment: let <pattern> = value
except its not really minification. its the removal of redundant information which is not actually relevant to understand the code.
for your lifetime example: the tick may be a small character, but within thw syntax, in the places where it can be used, its not actually misplaceable due to the surrounding syntax, making a more verbose way of marking a word as a lifetime name quite redundant.
While yes, in many cases the concrete type isn't directly readable from source, you don't actually want to have to specify the type everywhere as that would get extremely verbose, and its always easy enough to find.
"multiline mutations", which i would call "method chaining", is a common pattern in many languages and proven to work well.
yes, if you try to write finished Rust programs without invoking the compiler once, and in a basic text editor, you'll likely have a bad time.
Its a language built with workflows in an IDE and utilizing compiler feedback in mind, and thats fine.
@risottobias @buherator i'm also very surprised that you think a language like Rust, where mutability has to be declared explicitly both for owned variables are references (pointers) obfuscates the mutability of things.
i can imagine some of the functional languages take this further, but amongst other multi-paradigm especially systems languages i cannot think of any language where mutability is treated as clearly and obviously as in Rust. the most widely used languages are much worse in this aspect (JS, Python, C, C++, Java, ...)
@risottobias @buherator i really do wonder what sort of mindset one has to approach Rust from to call it inconsistent. It's, syntactically, a far more consistent language than any other i know.
Things like everything being expressions with the semicolon making a expression evaluate to () instead of a specific value and separating expressions into statements is a level of consistency i constantly wish for when writing other languages.
Other langs may have some destructuring/unpacking, but in Rust every assignment is pattern matching (let, loops, even function args).
While writing Rust, i think theres only one part where i couldn't follow and appreciate the reasoning for a sytanx being the way it is: cfg macros. I feel those should be using operators for and, or, not, etc. instead of what looks like function calls.
What i quite understand tho is that its a language which doesn't really follow the same logic as many of the popular langs, i had to actually learn the syntax instead of just seeing similarities
@buherator ah, i think i finally understood part of your thought process:
you read "if", expect a normal "if <condition>", and then get whiplash from seeing a assignment in the <condition> place.
Thing is, a refutable assignment can fail, which you can think about as the condition evaluating to "false".
In words, i would put it as: "if this let matches and the assignment succeeds, do <block>"
or in the case of let-else "let name be val unless it doesn't match, in that case do <block>"
just trying to explain why I don't have an issue with the readability of if-let, and maybe why it was decided to implement it like this.
btw: the syntax you expect kinda exists as a macro: `if matches!(value, <pattern>) { <block> }` but it can't do assignments.