Live data from Hacker News

Weird Expressions in Rust

wakunguma.com

131–140 of 155 posts

Re: Weird Expressions in Rust

#131
post #123

Earlier quoted context omitted.

Scala and Haskell are there and I think they inspired this in Kotlin and Rust. In Haskell it's "bottom" and in Scala it's "Nothing". In Scala no one uses "return" (mostly because we don't care about performance in the same way), but if you do, the way it is internally implemented is by throwing exceptions, so in a sense it suffers from the same problems as Rust. It's actually very important to have that type in a lan…

? This works fine in a type system without an explicit bottom type. In Haskell or ML or whatever `emptyList` would be given a polymorphic type, `List a` or `'a list`. There's issues around doing this with mutable collections (i.e. the value restriction) but that's not what you're referring to...

> In Haskell or ML or whatever `emptyList` would be given a polymorphic type, `List a` or `'a list`.

That alone would not work. Think about it: `List a` means "A list that contains values of type `a` and `a` can be any type whatsoever". Now imagine you combine that list with a list of integers. That obviously cannot work, since `(++) :: [a] -> [a] -> [a]` as you see, the types must align.

The way Haskell fixes that is (apparently) by doing something called `Let-generalisation` (https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/let_...)

To me that feels like hacky way to exactly resolve the problem that I described, and if you turn it off then that code would stop working and fail to compile as expected.

Re: Weird Expressions in Rust

#132
post #85

Earlier quoted context omitted.

> "return " is not just an expression which can be typed. Yes, it is, and it can. It has the type !, no matter the type of .

It only has type !, if the return type of the lexically enclosing function declaration has the same type as that of , otherwise it's illformed. For any expression NOT involving "return", I can write, for example: const Z = but I cannot if contains a return embedded somewhere. The existence of a "return" somewhere in an expression changes the character of the entire expression. I.e. there are two classes of "expressio…

> For any expression NOT involving "return", I can write, for example:

> const Z =

> but I cannot if contains a return embedded somewhere.*

Sure, but that's not special about this case at all. I also can't write 'break' or 'continue' when I'm not inside a loop. When declaring a 'const', I am lexically not inside a function body, so I can't use 'return', which makes sense (the compiler will even tell you, "return statement outside of function body").

Particular statements being allowed in some contexts but not in others is entirely normal.

> My point is that none of this is necessary at all

Maybe it's not necessary, but I like the consistency this provides ("everything has a type"), and I imagine the implementation of the type checker/inferer is more straightforward this way.

Sure, you could define the language such that "a 'return' in a position that expects a typed expression will not affect other type that need to match with it" (or something else, in better, formal language). Or you can just define those statements to have the 'never' type, and not worry about it.

But ok, let's agree that it's not necessary. Then we're just talking about personal preferences, so there's no right or wrong here, and there's no point in arguing.

Re: Weird Expressions in Rust

#133
post #98
post #85

Earlier quoted context omitted.

It only has type !, if the return type of the lexically enclosing function declaration has the same type as that of , otherwise it's illformed. For any expression NOT involving "return", I can write, for example: const Z = but I cannot if contains a return embedded somewhere. The existence of a "return" somewhere in an expression changes the character of the entire expression. I.e. there are two classes of "expressio…

You can write it just fine if `const Z` is itself nested inside a function definition. And this isn't really any different from variable references, if you think about it. If you have an expression (x + 1), you can only use it somewhere where there's an `x` in scope. Similarly, you can only use `return` somewhere where there's a function to return from in scope. Indeed, you could even make this explicit when designin…

> You can write it just fine if `const Z` is itself nested inside a function definition.

You can't, actually: 'const' is special in that it's not considered by the compiler to be inside a function definition, even if it is (and the compiler will tell you, "return statement outside of function body").

But that doesn't invalidate your point; in a way it supports it: 'return' can only be used in function contexts, just like 'continue' or 'break' can only be used in loop contexts.

Re: Weird Expressions in Rust

#134
post #43
post #25

Earlier quoted context omitted.

If 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.

What'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…

Eh, I'm not sure I agree here. This feels sort of along the lines of, "well C is safe because someone can review your code and reject it if you try to dereference a possibly-NULL pointer".

The point of a language that is "safe" along some axes is that it makes those unsafe things impossible to represent, either by omitting an unsafe feature entirely, or making it a compile-time error to do unsafe/unsound things.

I will admit that this is something of a grey area, since we're talking about logic errors here and not (for example) memory-safety bugs. It's a bit muddier.

In general, though, I do agree that people should write code that is reasonable to read, and if a reviewer thinks some code in a PR is incomprehensible, they should reject it.

Re: Weird Expressions in Rust

#135
post #31

Earlier quoted context omitted.

> 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.

"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.

I 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.

Re: Weird Expressions in Rust

#137
post #123

Earlier quoted context omitted.

? This works fine in a type system without an explicit bottom type. In Haskell or ML or whatever `emptyList` would be given a polymorphic type, `List a` or `'a list`. There's issues around doing this with mutable collections (i.e. the value restriction) but that's not what you're referring to...

> In Haskell or ML or whatever `emptyList` would be given a polymorphic type, `List a` or `'a list`. That alone would not work. Think about it: `List a` means "A list that contains values of type `a` and `a` can be any type whatsoever". Now imagine you combine that list with a list of integers. That obviously cannot work, since `(++) :: [a] -> [a] -> [a]` as you see, the types must align. The way Haskell fixes that i…

You are misreading the quantification, a value l of type List a means that for all type a, the element of the list has type a. In other words, this is an universal quantification whereas your interpretation is an existential quantification.

This is obviously only possible if the list itself has no elements, and indeed a simple proof is that the statement above is valid for the empty type: all elements of a list of type List a have type empty (among other types). Thus there are no elements in this list.

And both Haskell or OCaml can prove it:

     type empty = | (* this is defining a never type *)
     type polymorphic_list = { l: 'a. 'a list } 
     (* OCaml require to explicit construct polymorphic type *)

     let polymorphic_lists_are_empty ({l} : polymorphic_list ) =
     match (l:empty list) with
     | [] -> () (* this is the empty list *)
     | _ -> . 
     (* this clause requires to the OCaml typechecker to prove that the remaining cases are unreachable *)

Re: Weird Expressions in Rust

#138
post #134
post #43

Earlier quoted context omitted.

What'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…

Eh, I'm not sure I agree here. This feels sort of along the lines of, "well C is safe because someone can review your code and reject it if you try to dereference a possibly-NULL pointer". The point of a language that is "safe" along some axes is that it makes those unsafe things impossible to represent, either by omitting an unsafe feature entirely, or making it a compile-time error to do unsafe/unsound things. I wi…

I think these situations are very different, because Weird Rust affects only weird code, while unsafety of C affects regular C code.

The difficulty in reviewing pointer dereferences is in reasoning about potential program's states and necessary preconditions, which C won't do for you. You can have neatly written C using very simple syntax, and still have no idea if it's safe or not. Solving that lack of clarity requires much than syntax-level changes.

OTOH the Weird Rust examples are not a problem you get in your own code. It's a local syntax problem, and it doesn't require complex whole-program reasoning. The stakes are also lower, because you still have the same safety checks, type checks, automatic memory management, immutability. The compiler aggressively warns about unreachable code and unused/unread variables, so it's not easy to write undetected Weird code.

Rust tried having Underhanded Code Contest, but it has been very Underwhelming.

Re: Weird Expressions in Rust

#139
post #13

Earlier quoted context omitted.

Yes, but why is it evil?

I think that part is a reference to a Futurama episode where a holodeck malfunction materialized several villains, including "Evil Lincoln".

Ah if that's the case, then that makes more sense. Thanks!

Re: Weird Expressions in Rust

#140
post #133
post #98

Earlier quoted context omitted.

You can write it just fine if `const Z` is itself nested inside a function definition. And this isn't really any different from variable references, if you think about it. If you have an expression (x + 1), you can only use it somewhere where there's an `x` in scope. Similarly, you can only use `return` somewhere where there's a function to return from in scope. Indeed, you could even make this explicit when designin…

> You can write it just fine if `const Z` is itself nested inside a function definition. You can't, actually: 'const' is special in that it's not considered by the compiler to be inside a function definition, even if it is (and the compiler will tell you, "return statement outside of function body"). But that doesn't invalidate your point; in a way it supports it: 'return' can only be used in function contexts, just…

You can do this:

   const ONE: i32 = { const fn foolish() -> i32 { return 1 } foolish() };
But yes, your larger point is exactly correct, the constant, even if it happens to be defined inside a function body, is not itself inside a function body and so we obviously can't return from it. It is also not inside an expression we can break out of (Rust allows you to break out of any expression, not just loops). It's a constant, like 5 is a constant, or 'Z' is a constant - this is not C or C++ where "const" means "actually a variable".
Post reply on HN