16 ms·
Rust Weird Expressions
- skohan 5y agoI have no idea what I am reading
- phaylon 5y agoIt's the part of the Rust compiler test suite that makes sure odd syntactical combinations still behave as they are supposed to.
- chrismorgan 5y agoAnd I guess it’s 2015 edition, because it’ll fail in 2018 edition Rust: where the 2015 edition has ::u8(0u8), the 2018 edition will need you to write crate::u8(0u8) or self::u8(0u8), since only crates exist at the top level now, rather than the contents of the current crate as well.
- Macha 5y agoHowever, the compiler has to continue to be able to compile 2015 edition code, as it is still guaranteed to work and be intermixable with 2018 (and soon, 2021) code in the same application.
- chrismorgan 5y agoFor curious people unfamiliar with the situation with this: Rust cares about backwards-compatibility so that you should still be able to compile code from 2015 in 2042 (some libraries are just stable!), but it also wants to allow certain changes to the language that you might think would be backwards-incompatible. Each crate (library) declares an edition, and the compiler follows the rules of that edition, and then you can link all the different crates together without worrying what edition they were written in. Editions are limited in what they can change, because things like traits are shared between crates, so they can’t be changed in editions; it’s mostly syntactic stuff, like the 2018 edition making async/await keywords and making dyn a full (rather than conditional) keyword.
- peterkelly 5y agoThis is what Python should have done.
- egeozcan 5y agoJavaScript had something close, valid on the context level, with "use strict" (which is, I guess, borrowed from Perl), and I still don't understand why they don't repeat that for newer features that would be much simpler if they broke backwards compatibility.
- winstonewert 5y agoThis approach only works for syntax level changes, it wouldn't have helped the problematic changes in Python 2 -> 3.
- cyphar 5y agoWell, the ability to specify python version by module would've made migration much easier for everyone (in theory). But you're quite right that it wouldn't by itself be a solution -- it would also have required additional complexity to handle interoperability when calling between python versions, both in the runtime and the programs themselves (even a sufficiently smart compiler can't figure out what string encoding a python2 function expects).
- fat-chunk 5y ago
- iudqnolq 5y agoThere's even a handy macro for switching between them /s https://twitter.com/m_ou_se/status/1392200805168689154 https://twitter.com/m_ou_se/status/1392200805168689154
- phaylon 5y agoYeah. I'd assume a 2018 specific one would show up below https://github.com/rust-lang/rust/tree/master/src/test/ui/rust-2018 https://github.com/rust-lang/rust/tree/master/src/test/ui/ru... if needed.
- est31 5y agoYeah it's indeed the 2015 edition because while compiletest watches for // edition:something comments [0] (like this [1]), it doesn't pass any edition flag to the compiler if no such comment is present, and the rust compiler defaults to the 2015 edition if none was specified. [0]: https://github.com/rust-lang/rust/blob/69b352ef7749825abde2d8f8e31c05f681e61a10/src/tools/compiletest/src/header.rs#L439 https://github.com/rust-lang/rust/blob/69b352ef7749825abde2d... [1]: https://github.com/rust-lang/rust/blob/69b352ef7749825abde2d8f8e31c05f681e61a10/src/test/ui/editions/edition-imports-2018.rs https://github.com/rust-lang/rust/blob/69b352ef7749825abde2d...
- berkes 5y agoMe neither. But I'd guess it's to test (or explain, or document) edge-cases or unexpected behaviour when using Rust std.
- Minor49er 5y agoI've seen those sorts of things in other languages, though they're often accompanied by explanatory comments, if not a separate document. Given that these appear to be in a test suite for Rust itself, I'm surprised that these aren't explained in the code (at least as far as I saw)
- tux3 5y agoAnother fun one (and an easter egg): fn main() { break rust; } This breaks rust =)
- linkdd 5y agoMy favorite is the punch card example.
- Lvl999Noob 5y agoHow does that one work?
- steveklabnik 5y agofn punch_card() -> impl std::fmt::Debug { This is a function that returns some type that implements Debug. Not specifically named. The rest of it is a combination of two syntaxes: .. and ..= These are both ranges. The former exclusive, the latter inclusive. You can write something like 1..5 (or 1..=5) to get a range from 1 to 5 inclusive/exclusive, but you can also leave the start or end off: 2.. will give you from 1 till the end, ..=5 will give you from the start to 4. This means that .. will give you the full range. It looks more normal when used in context: fn main() { let s = "some string"; let some = &s[..=4]; // you'd probably write ..5 but i wanted to show both syntaxes let string = &s[5..]; let all = &s[..]; // a no-op here but useful if s were a String and you wanted 'all' to be a &str println!("{}", some); println!("{}", string); } prints "some" and then "string". So, here's the tricky bit: you can overload what the range is over. They're not just for numbers. So: fn main() { let mut string = 'a'..='z'; let a = string.next(); let b = string.next(); println!("{:?}", a); println!("{:?}", b); } prints "Some('a')" and then "Some('b')", because iterators return Options. So... all of this means that, you can implement a range of ranges. And so this string builds up a ridiculous unbounded range of unbounded range of unbounded range of ...
- bruce343434 5y agoSo, semantically, what is `f(return)`? Are these examples supposed to actually do anything, or mirror actual code situations? Or is it just putting a monkey in front of a typewriter?
- chrismorgan 5y agoIn Rust, just about everything’s an expression. `return` is an expression that diverges, meaning it’s of type never (spelled !, and coercible to any type, since you know the value will never actually be used). Here are another couple of ways of writing the same general concept: f({ return; () // () in this case because f takes a parameter of that type }); let x = return; f(x); So f() will never actually be called. You’ll get an unreachable_code warning on f. As a minimal case it’s mostly just funny, but the underlying concept does find its way into real Rust code, mostly in generated code (such as with macros). Also, although just about everything’s supposed to be an expression, sometimes things that can be an expression or a statement go a little bit funny, and return is a prime candidate for things going wrong in weird ways in the compiler frontend or in code generation or something, so it’s worthwhile having such tests (there will be more involved tests of return specifically elsewhere).
- skohan 5y agoSo does this mean if I call `f(return)`, it will return from the calling context without ever calling the function `f`?
- ben0x539 5y agoyes, it's roughly comparable to { let x = return; f(x) } where the call to f is clearly happening after an unconditional return. A slightly more practical example might be something like f(if blah() { 42 } else { return }) which works out to, roughly, { let x; if blah() { x = 42; } else { return; } f(x) } To get realistic, if you write f(g()?) you basically get f(match g() { Ok(x) => x, Err(e) => return Err(e), }) which is how rust does error propagation without exceptions and without constantly repeating a golang-style mantra.
- mjbrusso 5y agoWouldn't it be time to create a International Obfuscated Rust Code Contest (IORCC)?
- linkdd 5y agoIsn't it just a normal Rust program ?</troll>
- steveklabnik 5y agoWe ran something similar in 2016: https://blog.rust-lang.org/2016/12/15/Underhanded-Rust.html https://blog.rust-lang.org/2016/12/15/Underhanded-Rust.html
- smoldesu 5y agoI like the idea, but I think Rust is too picky of a language to use for something like that. IOCCC is fun because of how interesting it is to watch seasoned C professionals finesse their favorite language. A Rust variant would likely end in tears, copious compiler warnings and perhaps blood.
- lmkg 5y agoI'm shocked that this doesn't have my own personal favorite: fn eject() -> i32 { return return return return return return!!!!!!!!11; }
- woah 5y agoWhat is this?
- pitaj 5y ago`!` is binary negation for integers. `return x` is an expression that can be used in place of any type because it will never be used.
- masklinn 5y ago> `return x` is an expression that can be used in place of any type because it will never be used. To clarify: the expression will run, but it has the type `!` ("never"). And while that's still not a "proper" rust type (other uninhabited types are though they may not be quite as special-cased in the compiler), it is in essence a bottom type: since it can't have a value, it will unify with any type. That's why Rust is perfectly happy with let foo = if a { 1 } else { return 42 }; The first branch has type `{integer}`, the second branch has type `!`. `!` unifies just fine with `{integer}`, therefore the typechecker is happy.
- OJFord 5y agoWhaat, was this new in 2018 ed? I could've sworn it was `()` and so you couldn't do that. I don't write rust that often, and happen to be reading this just as I've written some code that's.. not that messy but not that I'm pleased with, wrangling around exactly this sort of if-assign-else-return pattern.
- steveklabnik 5y agoI don’t believe it should be new, but this can happen by accident sometimes. Like, if you included a ; on the non-return branch it would return () overall and you probably thought the return did it when it wasn’t that. Hard to say!
- steveklabnik 5y agoFour years ago I wrote about my favorite example in this file: https://news.ycombinator.com/item?id=15495027 https://news.ycombinator.com/item?id=15495027 Some things only make sense if you know very old Rust, and they've just been updated as the language has been updated, obfuscating their original meaning.
- doggodaddo78 5y agoGood god. Wicked, bad, naughty Rust. Oh, it is a naughty language, and it must pay the penalty -- and here in Castle Congruence, we have but one punishment for setting alight the grail-shaped semantics.
- Zababa 5y agoI'd like to add the "Bastion of the Turbofish", which is my personal favorite: https://github.com/rust-lang/rust/blob/master/src/test/ui/bastion-of-the-turbofish.rs https://github.com/rust-lang/rust/blob/master/src/test/ui/ba...
- iudqnolq 5y agoTo explain this, since it took me a minute: Rust has a syntax called "turbofish" for disambiguating ambiguous generic calls. (Coined by Anna Harren on twitter, the first reply is an illustration from Karen Rustad Tölva, the inventor of Ferris [1]). Although Vec is generic over any item type, you can normally just write let foo = Vec::new(); If you're doing something complicated where the compiler can't infer the type, you instead use the turbofish syntax let foo = Vec::<u32>::new(); Some people hate this syntax. One of the better arguments I've seen against it comes (I think) from ManishEarth, who points out that it's very hard for the compiler to detect that incorrect attempts at writing a turbofish are in fact attempts at the turbofish and offer help. That test case (minus some excellent poetry) is let (oh, woe, is, me) = ("the", "Turbofish", "remains", "undefeated"); let _: (bool, bool) = (oh<woe, is>(me)); That is actually comparing the string "the" against the string "Turbofish" and the string "remains" against the string "undefeated". The test case is there to point out that if the "::" in the turbofish is removed (Vec<u32>::new() instead of Vec::<u32>::new()) there will be cases where it's ambiguous. [1] https://twitter.com/whoisaldeka/status/914914008225816576?lang=en https://twitter.com/whoisaldeka/status/914914008225816576?la... EDIT: Fix my misspelling of ManishEarth
- eslaught 5y agoSee also this comment that makes the ambiguity explicit: https://github.com/rust-lang/rfcs/pull/2527#issuecomment-414635205 https://github.com/rust-lang/rfcs/pull/2527#issuecomment-414...
- IainIreland 5y agoFor the record, I suspect "Manis Heath" is in fact "ManishEarth", aka Manish Goregaokar.
- galangalalgol 5y agoI really want to like rust but it seems so very complicated. C++ is too but I have been growing with it. It has been my primary language for 27 years.
- larschdk 5y agoI often feel the same, and there is certainly a bit of learning curve, but in my experience, all the time you spend battling the compiler in Rust is time saved from debugging in C++, several times over. I'm repeatedly surprised by how my code just works when it finally compiles. It's like the compiler is telling you, "you just made a bug".
- skohan 5y agoJust to give an alternate viewpoint, I'm currently writing most of my code in Rust, and while I am generally a fan of the language, and a huge fan of the tooling and community, I think Rust often gets a pass on complexity because it's mostly compared against C++ which is itself a monstrosity. There are many, many other languages to which Rust seems insanely complex and difficult to program in by comparison. I think that often gets blamed on accommodating the borrow checker, and Rust's low-level nature, but I think a lot of it is avoidable.
- steveklabnik 5y agoI'm curious, not saying you're right or wrong: what bits do you think are avoidable? Personally, I agree that Rust is complex, but almost all of that complexity is inherent to the kinds of tradeoffs Rust makes. With different tradeoffs, I can certainly imagine a much simpler language, but it's less clear to me what stuff could be significantly simplified without doing so. However, I am extremely biased!
- skohan 5y agoMy go-to example would be the module system: 1. 90% of the time, rust module layouts are basically a copy of the file-system heirarchy, so why do I have to type this? This system would be much more approachable if it gave the file-system hierarchy as the default module layout, and let you override it explicitly. The argument I have heard against this is that some people like to comment out module declarations during debugging. I don't find this compelling, because I don't see why you couldn't have an explicit way to ignore a module instead. This is just one of many cases where Rust seems to prioritize the edge case at the expense of the common case. 2. On top of requiring a lot of boilerplate the module system is quite esoteric. I've had to learn it twice: a few years ago I was dabbling in Rust, and I remember having to struggle a bit to understand how to add a second file to my project, and then about a year ago when I was getting back into rust I found it unintuitive a second time. I challenge you to find someone who is unfamiliar with the module system, and see how long it takes them, using only the documentation, to figure out where they have to put the `mod` declarations to add nested submodules to their project to make it compile. Maybe I am unreasonably thick, but I don't think it's my problem since I've worked with a lot of languages, and never had this much trouble. 3. This esoteric system isn't even deterministic. Imagine I have the line `mod foo` in my `lib.rs`. Where can I go to find the source for that? Well, it depends. It could be in `src/foo.rs`, or it could be in `src/foo/mod.rs`. And let's say I'm using external crate: `use some_crate`. Which import does that correspond to? It could be: `some_crate`, or it could be `some-crate` in my `Cargo.toml`. You just have to kind of know all of these implicit behaviors of the compiler to know what's going on. So this is just one feature, but IMO it's just one example of a case where Rust puts very little emphasis on the UX and understandability of the language.
- ivan888 5y agoLanguages like this: fun to author the grammar, and have the entire thing in your head. So expressive! Difficult for anyone else (including yourself in 2 years) to understand
- steveklabnik 5y agoVirtually any language can be "like this," these examples are all nonsense designed to stress the compiler. This code isn't supposed to be understood by any human.
- augustk 5y ago"Another lesson we should have learned from the recent past is that the development of richer or more powerful programming languages was a mistake in the sense that these baroque monstrosities, these conglomerations of idiosyncrasies, are really unmanageable, both mechanically and mentally. I see a great future for very systematic and very modest programming languages." -- "The Humble Programmer" https://www.cs.utexas.edu/~EWD/transcriptions/EWD03xx/EWD340.html https://www.cs.utexas.edu/~EWD/transcriptions/EWD03xx/EWD340...
- shakow 5y agoThe problem is that the complexity in computing is intrinsically irreducible. Sure, you can get nice nifty little languages, but then the latent complexity will be transferred into the program itself (e.g. program length, cyclomatic complexity, etc.) rather than in the language; that's why although they can technically do the same thing, we have a spectrum of languages ranging from assembly to APL. Practically, it's exactly what comes out of the infamous Rust vs. Go debate: for instance, the former prefers a short and concise albeit arguably more complex encoding and processing of the errors based on a monadic type, whereas the other leans toward a simpler, but more verbose, double return value + if/else/early return. Chose your poison.
- pcwalton 5y agoIs it complexity, or is it the result of the simplification of unifying expressions and statements in most cases?
- kubb 5y agoI mean the fact that you can do funny stuff with the syntax rules and the type system doesn't disqualify their usefulness. Rust's type system is impressive as it enables enforcing powerful constraints over how the code is allowed to execute, eliminating whole classes of bugs, and enabling elegant, zero-cost abstraction over swathes of algorithms and data structures, which in turn lets you have high quality libraries that provide to-the-point solutions for so many of the common tasks that a developer will deal with day to day. A more elegant language that solves the problems that Rust solves, with a production-ready implementation and a good ecosystem hasn't been presented to the world yet. It's to be expected that if you want to benefit from its advantages, you need to pay the cost of dealing with a more complex language. On the other hand paying the cost of being exposed to more concepts pays off by giving you more flexibility to craft simple solutions. Complexity of a project can vastly exceed that of the language the project is written it. In that case the cognitive cost of using more advanced tools to manage that complexity is a boon in the long run. This is analogous to any kind of more sophisticated technology, e.g. you can argue that a scythe is easier to use than a harvester, but with a big enough field, if you have the option, you'll go for the latter.
- iudqnolq 5y agoI'd like to nominate anything by Mara Bos (@m_ou_se) on Twitter. For example: > What's your favourite @rustlang boolean? Option<()>, Option<Option<!>>, std::iter::Once<()>, std::fmt::Result > tired: u16, wired: BTreeSet<BTreeSet<BTreeSet<BTreeSet<()>>>> > What's your favourite @rustlang integer? &[()], Enumerate<Repeat<()>>, Poll<File>, *const PhantomData<usize> @jaaaarwr> How is Vec<()> not in this poll? > That's just the owning version of &[()], right? Why would you need to own ()s? ;) Edit: She's also the author of inline_python!, a serious macro for interspersing code that calls python (including local variable interop), and whichever_compiles!, a joke macro that takes multiple expressions, forks the compiler (as in a fork syscall), and compiles the first one that compiles to valid rust.
- estebank 5y agoResult<(), ()> is just a spicy bool. A lot of people are on board with not using if/else and instead :) match x { true => {} false => {} }
- skrtskrt 5y ago> Result<(), ()> is just a spicy bool. EDIT: quoted wrong part of parent, ignore above > match x { true => {} false => {} } I really like this. Pattern matching syntax is great, nice to use in place of if/else as much as possible
- iudqnolq 5y agoI disagree. There's a clippy lint against pattern matching on bool. I also think Result has the connotation of "success or error". Plus it's always annoying when a function returns an error that doesn't implement Error and I have to wrap/replace it to make it integrate with the standard error handling ecosystem. There's a nice alternative though, which is empty struct errors. #[derive(Debug, thiserror::Error, displays of::Display)] /// Frobnicating the foo failed. struct FrobError; The advantage is the standard advantages types use, i.e. if I want to propagate it I know this specific zero sized type has a specific meaning. It'll also show a nice message if you print it.
- caturopath 5y agoWhat's weird about `fn evil_lincoln() { let _evil = println!("lincoln"); }`?
- steveklabnik 5y agohttps://news.ycombinator.com/item?id=27154557 https://news.ycombinator.com/item?id=27154557
- rabidferret 5y agoI have always wondered this!
- caturopath 5y agoThanks a bunch!
- tanaypingalkar 5y agoi think this = #[]; is for rust analyser, not sure , can anyone tell me what is this #[];
- steveklabnik 5y agoIt is called an "attribute" and it is part of the language itself. There is also #![]. The difference is what they apply to, #[] applies to the following thing, #![] applies to the parent thing. You'll see #![] to enable nightly features in Rust, for example, and #[] for things like custom derives.
- krick 5y agoYeah... I didn't want to admit that for a really long time, but Rust isn't really a huge success. You can obfuscate the code in most (all?) of the languages, but the fact that all of these things are even possible, definitely isn't something to be proud of.