7 ms·
they exist because whole language built to treat expressions as firstclass citizens : blocks, ifs, matches, even macros as expressions that return values. so on
by b0a04gl 1y ago
they exist because whole language built to treat expressions as firstclass citizens : blocks, ifs, matches, even macros as expressions that return values. so once you internalize that, all these weirdo one liners are artifacts. just artifact of a system where expressions compose infinitely. the syntax tree runs deeper than most people's habbits allow. you hit that depth and brain says this is wrong but compiler's allowing.
- gmueckl 1y agoIf a language that claims to be security focused is easily able to express constructs that human minds find barely comprehensible, or worse, then this is itself arguably a security issue: it's impossible to check the correctness of logic that is incomprehensible.
- PaulHoule 1y agoIt is a critique of macros. 500 lines of Common Lisp replaces 50,000 lines of C++ but those 500 lines make no sense at all the first time you see them.
- steveklabnik 1y ago> If a language that claims to be security focused Rust does not claim to be particularly security-focused, only memory safe. Also, this means that you'd consider any expression-based language to be inherently a security problem.
- gmueckl 1y ago"Memory safety" is an aspect of computer security. And security is the first listed value in rust's mission statement. Rust is not written as a pure expression based language. And as we all know very well from the experience with C and JS, any unexpected and weird looking code has the potential to hide great harm. Allowing programmers to stray too much from expected idioms is dangerous.
- steveklabnik 1y agoIt is an aspect, but it Rust does not promise your core is secure. It’s not purely expression based but it is very close to it, there’s only a few kinds of statements, the vast majority of things are expressions.
- keybored 1y agoWe would need an example of puzzling code that can easily hide (security) bugs. The submission shows weird program snippets. I don’t think it shows weird snippets that can also easily hide bugs?
- kelnos 1y agoI think you're looking at it a little backward. Or rather, with superset/subset confusion. Rust can say "we care about memory safety" without being security-focused. But Rust cannot say "we are security-focused" without also caring about memory safety. Being security-focused requires you to care about a laundry list of things, including memory safety. But on its own, caring about memory safety just means... you care about memory safety.
- pornel 1y agoWhat's the threat model? If you're reviewing untrusted or security-critical code and it's incomprehensible, for any reason, then it's a reject. Syntax alone can't stop sufficiently determined fools. Lisp has famously simple syntax, but can easily be written in an incomprehensible way. Assembly languages have very restrictive syntax, but that doesn't make them easy to comprehend. Rust already has a pretty strong type system and tons of lints that stop more bad programs than many other languages.
- gmueckl 1y agoMany modern language designers focus on shaping expressibility rather than providing the maximum possible flexibility because their designers learned from C, Lisp and other languages that made mistakes. Examples lamguages are Java, C#, D, Go... some arguably with more success than others. But language design that gave ultimate expressive power to the the programmer is a relic of the past.
- pornel 1y ago??? "Expressibility" and "expressive power" are vague and subjective, so it's not clear what you mean. I suppose you object to orthogonality in the syntax? Golang and Java definitely lack it. But you also mention C in the context of "maximum possible flexibility"? There's barely any in there. I can only agree it has mistakes for others to learn from. There's hardly any commonality between the languages you list. C# keeps adding clever syntax sugar, while Go officially gave up on removing its noisiest boilerplate. D has fun stuff like UFCS, template metaprogramming, string mixins, lambdas — enough to create "incomprehensible" code if you wanted to. You're talking about modern languages vs relics of the past, but all the languages you mention are older than Rust.
- gmueckl 1y agoHave you ever seen submissions to IOCCC or Underhanded C Code Contest? That is what too much syntactic flexibility looks like (if taken to the extreme). If you want your code to be secure, you need it to be correct. And in order for it to be correct, it needs to be comprehensible first. And that requires syntax and semantics devoid of weird surprises.
- int_19h 1y ago"never" is an easily comprehensible concept once you start asking the right questions. But also, all examples in TFA are very artificial convoluted code. Meaning that you can write things like these just like you can write something like &&&...x - but why would you? Actual real-world uses of this feature are all quite readable.
- derriz 1y agoThat sounds superficially reasonable to me and I'm all for regularity in programming language semantics but on thinking about it further, I actually think it's a design flaw. It makes no more sense to me for "return <expr>" to have a type than it does to make "if <expr>" or "break" or "{" or any other keyword to have a type. These are syntactic elements. Rust's type system is clearly inspired by Hindley-Milner and most languages using such a type system either don't even have a return keyword. Even if you disagree with this argument, this design decision has resulted in all these weird/confusing but absolutely useless code examples and there is no upside that I can see to this decision in terms of language ergonomics. What practical value is it to users to allow "return <expr>" to itself be an expression? That you can use such an "expression" as arguments to function calls with hilarious wtf consequences? It's a piece of syntactic sugar.
- steveklabnik 1y agoRespectfully, "it makes no sense to me" isn't an argument. if and break both have types in Rust as well. > don't even have a return keyword. This is because they are not procedural languages, it has nothing to do with the type system. > there is no upside that I can see to this decision in terms of language ergonomics. There's tremendous upside! That's why lots of languages choose this. For example, there is no need for the ternary in Rust: if can just do that. > What practical value is it to users to allow "return <expr>" to itself be an expression? Code like this just works: let guess: u32 = match guess.trim().parse() { Ok(num) => num, Err(_) => return, }; That is, if return wasn't an expression, we'd have a type error: the two arms would have incompatible types.
- derriz 1y agoSee my comment above, your example only "just works" if the enclosing function has the appropriate return type (in this case none). So the syntactic element "return" is not just an expression - unlike other sub-expressions, it involves action at a distance - i.e. it must not just agree with it's context as part of an expression but it must agree with the enclosing fn signature.
- throwawaymaths 1y agoits not just that some things you would usually think are control flow are expressions, its also that there are unusual rules around coercing the `noreturn` type.
- tialaramex 1y agoThe only "unusual" rule here is that Rust offers the zero type addition, but does not provide the (much more complicated) other type additions So Rust does have: String + ! = String But Rust doesn't have: String + i32 = Either<String,i32> Note that the never type ! isn't special here, Rust will also cheerfully: String + Infallible = String or if you were to define your own empty type like so: enum MyEmptyType {} // MyEmptyType has no possible values Now under type arithmetic String + MyEmptyType = String and indeed that works in Rust. Edited: Syntax fix
- deleted 1y ago[deleted]
- AIPedant 1y agoHmm my read is this is a slight overstatement - Rust was always built with the idea of expressions as first class citizens, but practicality and performance requires expression-breaking keywords like “return” which don’t fit neatly in an ML-ish language and have a few plain old hacks associated with implementing them (not “hack” as in lacking robustness; I mean theoretically/formally inelegant). Likewise there’s some stuff (u8) which is a simple syntax quirk. But a lot of these return/etc oddities are because Rust is ultimately an imperative language with strong influence from functional programming.
- efnx 1y agoI’ve found rust to be an ML in C’s clothing. The main difference from other MLs is the lack of higher kinded types, so it’s difficult to express things like Functor, Monad, Arrow, etc
- steveklabnik 1y agoreturn is an expression in Rust, and it fits in well. There are very few statements: https://doc.rust-lang.org/stable/reference/statements.html https://doc.rust-lang.org/stable/reference/statements.html and a lot of expressions: https://doc.rust-lang.org/stable/reference/expressions.html https://doc.rust-lang.org/stable/reference/expressions.html
- AIPedant 1y agoWe're speaking past each other since there's "expression" as defined in the Rust specification vs "expression" as in ordinary computer science, and Rust's use of return is certainly not an expression in the latter sense. It is shoehorned into being called an expression but it has no semantically meaningful type, it is an effect. A type is (carefully but somewhat arbitrarily) assigned to it, which is why some of those examples involving "return" are particularly goofy. It is not material for most programs since it only comes up with intentional misuse of the keyword. But "return" does not make sense in functional languages with true first-class expressions - functions don't return values, they get evaluated and the frame destruction / etc are all abstracted away. It makes sense in Rust because expressions in the CS sense of the term are ultimately not first class.
- nine_k 1y agoThis is logically sound, but pragmatically not so. I wish the compiler could issue a warning or even an error if an expression of type `never` is used in a logical condition, like that of an `if`. While such cases might have rare legitimate uses (e.g. some edge cases of macro expansion, etc), I'd like it to be marked explicitly, similar to `unsafe`, e.g. with some `allow_never_as_condition` marker. Likely the same should apply to expressions of type `()`.