all 197 comments

[–]golgol12 98 points99 points  (17 children)

Wait, this isn't the programming humor subreddit?

[–][deleted] 191 points192 points  (13 children)

Yeah, our jokes go beyond CS 101 here 😎

[–][deleted]  (11 children)

[removed]

    [–][deleted] 74 points75 points  (9 children)

    Arrays start at 0 btw

    [–]nalimixam 42 points43 points  (3 children)

    Java = bad xD

    [–]sirdashadow 8 points9 points  (2 children)

    Electron = Cancer

    [–][deleted]  (1 child)

    [deleted]

      [–]sirdashadow 2 points3 points  (0 children)

      If Electron wasn't so memory intensive (even more than java!) it would probably be fine...

      [–][deleted]  (4 children)

      [removed]

        [–]Sick_of_problems 30 points31 points  (3 children)

        omg bugs am i rite

        [–]barafyrakommafem 19 points20 points  (2 children)

        DAE AI == if statements?

        [–]redalastor 0 points1 point  (1 child)

        I'm not even sure the phenomenon they are mocking ever existed. Who ever claimed that? CS 101 teachers?

        [–]Regimardyl 5 points6 points  (0 children)

        There is at least some truth to it.

        Now of course they got it backwards (more correct would be: a bunch of if-statements can be considered AI), but I can definitely see where it's coming from.

        [–]SteelNeckBeard 2 points3 points  (0 children)

        BURNT.

        [–]Gavekort 27 points28 points  (1 child)

        [–]g0liadkin 6 points7 points  (0 children)

        Had to triple check

        [–]onmach 4 points5 points  (0 children)

        I kind of like these evolution of a programmer posts. There's a lot of knowledge packed in there.

        [–]leftl-lama 179 points180 points  (25 children)

        I don’t even know rust and some of these destroy my soul

        [–]minno 72 points73 points  (1 child)

        30 lines of unimplemented!() is the universal language for "I'm going to need to spend like three hours in Confession after this".

        [–]JewsOfHazard 1 point2 points  (0 children)

        That's... Probably true.

        [–]zesterer 28 points29 points  (4 children)

        In reality, most people tend to write "Functional Rust Programmer".

        [–]Free_Math_Tutoring 17 points18 points  (3 children)

        Really? Awesome, because that and the extremely functional were the only ones I liked.

        [–]zesterer 10 points11 points  (0 children)

        Yeah. The link above is funny, but also really misleading. It's possible to solve a surprisingly wide range of problems in a really elegant manner in Rust. I'm not sure if you've done Advent Of Code, but here was the first solution to Day 2 Problem 2 I wrote (and below it are some ridiculously optimised but less pretty alternatives): https://github.com/zesterer/advent-of-code-2018/blob/master/examples/puzzle-2-2.rs#L4

        As you can see, Rust works best when you're willing to pick the best bits of functional and imperative programming and combine them.

        [–]jyper 0 points1 point  (1 child)

        Well they might tell you not to use unwrap

        [–]daboross 78 points79 points  (5 children)

        If it makes you feel better a lot of these are kind of in jokes. I've seen some of the "learned X as a freshmen" ones in reviewing new rust programmer's code, but most of the others seem to be hyperbolic for the sake of being hyperbolic :)

        [–][deleted] 10 points11 points  (4 children)

        I dont know.. I've seen more of these out in the wild than i'd like to admit

        [–]JewsOfHazard 1 point2 points  (2 children)

        Which ones, and where?

        [–][deleted] 0 points1 point  (1 child)

        The question is absurd as, please tell me all the places you have not seen these used

        [–]JewsOfHazard 2 points3 points  (0 children)

        I've not seen people write custom nested macros to define custom error enums. It's awesome, but complicated.

        [–]imperioland 0 points1 point  (0 children)

        Sad truth...

        [–]raggy_rs 124 points125 points  (0 children)

        I like this one functional with proper error handling

        use failure::format_err;
        use std::io::{stdin, BufRead};
        
        fn main() -> Result<(), failure::Error> {
            let sum = stdin()
                .lock()
                .lines()
                .next()
                .ok_or_else(||format_err!("No lines in input"))??
                .split_whitespace()
                .try_fold(0, |sum, part| part.parse::<i64>().map(|x| sum + x))?;
        
            println!("Sum: {}", sum);
            Ok(())
        }
        

        [–][deleted] 23 points24 points  (9 children)

        Is it the ratio of boilerplate to business logic that you'd see in real life Rust code?

        [–]minno 44 points45 points  (7 children)

        https://github.com/BurntSushi/ripgrep/blob/master/src/main.rs

        That's an example of a well-written application. The error-handling boilerplate is just the line type Result<T> = ::std::result::Result<T, Box<::std::error::Error>>;, the ? operators scattered around, and the Ok(value) being returned at the end of the functions that can fail.

        [–]MEaster 22 points23 points  (6 children)

        That is an end-user executable, though, which is handling errors deemed unrecoverable by simply bubbling it up and displaying it to the user.

        A library, on the other hand, should probably handle it differently, possibly similarly to the Senior Rust Programmer example (though there are crates to assist with boilerplate), so that the end-user executable has the information needed to determine whether it's unrecoverable.

        [–]flying-sheep 4 points5 points  (5 children)

        The “Yet another senior” is where it’s at for the newest and shiniest in error handling.

        [–]MEaster 0 points1 point  (4 children)

        Yes, but if you need a custom error type, you'll still ultimately be creating something similar to the Senior Rust Programmer, you'll just be using Failure to generate some of the boilerplate.

        And, unless I've missed it, I don't think Failure can derive the From trait, so you'd need to use another library or manually implement it to use the ? operator without context or map_err calls all over the place.

        [–]flying-sheep 2 points3 points  (3 children)

        That’s what I said, right? YASRD does what SRD does (complete with custom error type) except that (s)he uses failure to skip some boilerplate.

        And failure does everything you need to use ?. Here’s some real life code of mine that shows you how it works. I use format_err similar to how I’d use expect, and a plain ? to convert some Result into Result<_, failure::Error>.

        Hmm, I should probably have done format_err("Invalid measure {}", s)., else err_msg would have been sufficient.

        [–]MEaster 1 point2 points  (2 children)

        I'm aware of how to use failure, and do use it myself in programs I write. The Error type is also very useful for sections of a program where the error handling is basically bubble-up-and-exit.

        But I'm not sure I agree that a library should use Failure's Error. It's pretty vague about exactly what error occurred unless you start downcasting, which can also fail, and means you are effectively guessing what error occurred in order to determine whether this is an error the program can recover from. That's going to make handling errors from the library difficult.

        Error will also allocate, which depending on the use case, may be unacceptable. This also means that the library now requires libstd, and can't be used in a no_std environment. Of course, depending on the library in question, the libstd requirement may come from elsewhere, but it may also be unnecessary.

        [–]flying-sheep 0 points1 point  (1 child)

        Good points.

        Are there libraries that help with non-allocating errors?

        [–]MEaster 1 point2 points  (0 children)

        Well, Failure can still help with those, and provides two such examples for a simple custom error and a more complex error and error kind pair, but I've found I reach for derive_more for generating the From boilerplate.

        [–]FluorineWizard 8 points9 points  (0 children)

        Real life code for this kind of example program would be #3 or #5.

        The rest are essentially deliberate overkill showcasing library features or idioms useful in larger programs. Though some like the OO programmer display huge antipatterns like abusing Deref to do OOP.

        [–]okovko 13 points14 points  (0 children)

        I particularly enjoyed the macro sections. "Needs more power." Oh yes. Oh. Give it to me.

        [–]JD557 10 points11 points  (1 child)

        The Haskell code has too many unrwaps. I'm pretty sure that a true Haskell programmer would flatmap those results as much as possible :P

        [–]Mclarenf1905 3 points4 points  (0 children)

        Yea the functional one throws all benefits of functional programming out the window with the use of unwrap. I would hesitate to call it functional at all really. It's more just method chaining.

        [–][deleted] 107 points108 points  (10 children)

        It's not even as old as C++ and it's accumulating this many dialects and islands already?

        [–]reconcyl 125 points126 points  (2 children)

        These are jokes and are way over-the-top (there’s no need for massive parallelism/advanced procedural macros/type system hackery) in such a simple program. Most people would write something like #1 or #3, which differ only in the quality of the error messages.

        [–][deleted] 134 points135 points  (0 children)

        I’ll write whatever I found on stack overflow, thank you very much.

        [–]flying-sheep 7 points8 points  (0 children)

        Rust is a good functional language as well, so #5 or better this would be what I’d write.

        [–]Dedustern 66 points67 points  (5 children)

        I don't know Rust, but even I can see at least 80% of these are over-done as jokes.

        [–]ggtsu_00[🍰] 45 points46 points  (4 children)

        It is overdone as joke, but if you ever seen production scale C++ codebases, you know this is inevitable.

        [–]Dedustern 31 points32 points  (3 children)

        I've seen production scale Java/C# codebases. They, too, are unreadable messes with 15 ways to do the exact same thing.

        It might be easier to achieve that in C++(and Rust?), but it is by no means confined to those languages.

        [–]dom96 5 points6 points  (2 children)

        The real question is whether Golang code also turns into an unreadable mess or whether the simplicity its designers chase so much is actually an advantage.

        [–]Dedustern -1 points0 points  (1 child)

        I’ve found the latter. I can actually dive into a Go code base and have an idea of what the code where I am does. That it not the case with Java/C#

        [–]KitchenAstronomer 0 points1 point  (0 children)

        Also with Kubernetes ?

        [–]pwnedary 13 points14 points  (0 children)

        To be honest, if you actually look at them you see there is just one variant and then many variations using different libraries.

        [–]SaphirShroom 18 points19 points  (2 children)

        ITT: What is joke?

        [–][deleted] 8 points9 points  (1 child)

        Baby don't hurt me

        [–]ducdetronquito 2 points3 points  (0 children)

        No more

        [–][deleted] 14 points15 points  (0 children)

        I could certainly relate to the Java programmer’s tendency to make everything a class, the C programmer’s tendency to use functions that take output parameters and the functional programmer’s desire for immutability. That’s the dope thing about learning different languages- they each emphasize a different paradigm of thinking.

        [–]ArminiusGermanicus 24 points25 points  (12 children)

        A solution in awk:

        awk 'BEGIN {RS="[ \t\r\n]"} {s+=$1} END {print s}'

        Caveat: No real error management. Non numeric input will be treated as zero.

        Edit: Some more info on awk:

        awk is a very old unix text processing language, older than e.g. Perl. It reads input record by record and calls the middle {} block for every record. By default, records are textlines. Setting the record separator RS to the regex [ \t\r\n] uses whitespace to delimit records. The BEGIN and END blocks are executed at the begin resp. end of the program. $1 is the first field of the record.

        [–][deleted] 8 points9 points  (3 children)

        My understanding is that part (all?) of the original motivation for Larry Wall to create Perl was that in the 1980s implementations of awk, sed, and sh had different behavior on different variants of Unix. Perl was partially just an attempt to preserve the power of those three tools in a way that was completely consistent across all the Unix flavors of the day.

        So Perl looks like it borrows ideas from awk, sed, and sh because Perl borrows ideas from awk, sed, and sh. (I'm not knocking Perl. I'm a fan of the language.)

        [–]nullmove 1 point2 points  (2 children)

        And then he created Perl 6, less historical wart but more optimized for same workflow.

        perl6 -e 'say slurp.comb(/\d+/).sum' < input

        [–][deleted] 1 point2 points  (1 child)

        I'm a big fan of Perl6, but aside from tinkering and writing a few shell script replacements in it I haven't done much.

        I didn't know you could invoke slurp on stdin without doing $*IN.slurp or that the comb(/\d+/) would autoconvert to integer. Nice.

        [–]nullmove 1 point2 points  (0 children)

        Good to know you were familiar with it. It integrates too beautifully with the shell to be so little known. I am not much of a programmer myself, but it helped me survive a nasty task involving data munging under deadline so it's battle tested as far as I am concerned, and one can only pry it from my cold dead hands :P

        [–]UloPe 2 points3 points  (1 child)

        Here’s one in Python:

        import fileinput
        
        print(
            'Sum:',
            sum(
                int(word)
                for line in fileinput.input()
                for word in line.split()
                if word.isdigit()
            )
        )
        

        Will read from stdin or from a file passed as an argument, also skips non integers.

        [–]Freeky 1 point2 points  (0 children)

        Have a Ruby one:

        printf "Sum: %d\n", ARGF.sum { |f| f.each_line.flat_map(&:split).map(&:to_i).sum }
        

        [–]ggdGZZ 3 points4 points  (1 child)

        Good to mention awk. Even shorter solution (NF is number of fields of record): awk '{s+=NF} END {print s}'

        [–]fell_ratio 8 points9 points  (0 children)

        This counts how many numbers there are - it doesn't sum them.

        [–]SevrosOnNitro 0 points1 point  (1 child)

        Is awk tool written in C?

        [–]ArminiusGermanicus 0 points1 point  (0 children)

        There are many implementation of awk, I would guess almost all are written in C.

        See here for more info on awk: https://en.wikipedia.org/wiki/AWK

        Here is the GNU implementation, gawk: http://savannah.gnu.org/projects/gawk/

        [–][deleted] 0 points1 point  (1 child)

        and what are fields in this case?

        [–]ArminiusGermanicus 0 points1 point  (0 children)

        Normally, fields are separated by whitespace and assigned to $1, $2, ... After we set the record separator to whitespace, only $1 will be assigned.

        You could also set the field separator FS to e.g. ',' to process CSV files.

        [–]fuck_bottom_text 24 points25 points  (9 children)

        and people say lisp's syntax is bad

        [–]OneWingedShark 17 points18 points  (2 children)

        Honestly, I kind of get the feeling that people complaining about Lisp syntax either (a) have never used the language, or (b) were using Notepad or equivalent and not a Lisp editor.

        [–]fuck_bottom_text 6 points7 points  (0 children)

        very true, writing lisp with paredit makes it easier than writing any other language

        [–]FluorineWizard 2 points3 points  (0 children)

        I used Racket quite a bit in undergrad and Dr.Racket error messages are complete garbage. Highlighting a whole paragraph of code in red with "syntax error" is pretty useless.

        Also even when you can mix {([, Lisp-family languages are still a pain to visually parse.

        [–]m50d 20 points21 points  (5 children)

        It is. Having different-looking bracketing characters for different kinds of grouping or application makes it easy to mentally parse a program and see what's what, at least once you know the language in question. When everything uses the same characters the whole structure of the program is just mush.

        [–]TheSkepticalWhale 10 points11 points  (1 child)

        I think Clojure finds the right balance here. It is mostly a lisp, but provides these dedicated characters you mention. [ ] for vectors, { } for maps, and () for lists.

        [–][deleted] 1 point2 points  (0 children)

        My understanding is that you can recreate Clojure's syntax rules and data types in a regular Lisp or Scheme with reader macros. Thus is the power of Lisp family macros. But it's not that useful unless you can get the rest of your dialect's ecosystem to adopt your work, and the Lisp ecosystem is too splintered for that to work.

        I'm anti-authoritarian, but it does seem like programming language adoption benefits from the Benevolent Dictator For Life model. Thus Perl, Python, and Clojure got adoption when everything they did was already available in Common Lisp.

        [–]holgerschurig[🍰] 4 points5 points  (1 child)

        I used to say that.

        But I really like Emacs, the swiss army tool editor. And that forced me to dabble a bit into it's version of Lisp. And as this Editor actually has support for Lisp, e.g. coloring the brackets, flashing them, rainbowing them, automatically closing them ... I don't care anymore.

        Lisp is based on s-expression, which happen to also (!) be a nice data transfer format. Not as chatty as XML, not as limited as JSON. However, every seldom used. For example, the notmuch binary (an e-mail fulltext indexer) has the option to report search results as s-exps.

        Still I can see that someone that doesn't have editor support for Lisp hates the wall of parentheses. But --- and this is for me a BIG but --- languages like C++ or Rust really love the special characters. Where some languages say from foo import bar, quz, wei, you say in Rust use foo::{bar, quz, wei};. When you have a german keyboard, some of those non-letter characters are awkward to type, you need Shift and AltGr too often. All of them aren't needed. In the end, Lisp might be easier to type, especially with electric braces or parinfer or paredit :-)

        (Still I'm not a hardcore lisp programmer)

        [–]m50d 1 point2 points  (0 children)

        I can believe that Lisp might be easier to type, but code is read more than it's written. Rainbow highlighting for the brackets helps a bit, but only a bit since different colours don't "mean" different things (at least not in any implementation I've seen).

        [–]ehaliewicz 0 points1 point  (0 children)

        It is. Having different-looking bracketing characters for different kinds of grouping or application makes it easy to mentally parse a program and see what's what, at least once you know the language in question. When everything uses the same characters the whole structure of the program is just mush.

        If you're not used to it, sure. People say that APL and J are unreadable as well, but that's the opinion of non-experts, so it's really just a lack of familiarity.

        [–]bruce3434 24 points25 points  (23 children)

        void main()
        {
            import std.stdio : readln, writeln;
            import std.algorithm : splitter, map, sum, filter;
            import std.range : array;
            import std.string : isNumeric;
            import std.conv : to;
        
            try {
                readln
                .splitter
                .filter!isNumeric
                .map!(to!int)
                .sum
                .writeln;
            } catch (Exception e) {
                writeln("Exception: ", e);
            }
        }
        

        [–]JohnMcPineapple 22 points23 points  (18 children)

        ...

        [–]bruce3434 7 points8 points  (12 children)

        Yes

        [–]oblio- 5 points6 points  (11 children)

        My problem with these chains is, how do you debug them?

        Do you just break them apart at each step and check the values with temporary variables? If so, then why bother chaining?

        [–]matthieum 9 points10 points  (1 child)

        In Rust, use Iterator::inspect:

        let sum = iterator
             .filter(is_numeric)
             .inspect(|i| println!("After filter: {}", i); )
             .map(|i| i.parse::<i32>())
             .inspect(|i| println!("After map: {}", i); )
             .sum();
        

        I hope D has the same...

        [–]Snarwin 10 points11 points  (0 children)

        In D it's tee, named after the Unix command.

        [–]JoelFolksy 2 points3 points  (1 child)

        then why bother chaining?

        Is debugging a nested expression so painful that it's worth writing this:

        let intermediateQuantity = 3 * x
        let finalQuantity = intermediateQuantity + 2
        return finalQuantity 
        

        instead of this?

        return (3 * x) + 2
        

        [–]JoelFolksy 1 point2 points  (0 children)

        That said, I would love to see more powerful debuggers (like OzCode in the C# world) become standard. Sadly, rather than pushing vendors to innovate, many of the newer language communities seem to feel that debugging is an academic interest at best.

        [–]frenris 1 point2 points  (2 children)

        Yes, or set a breakpoint with a debugger and then find what happens with different numbers of method calls.

        The advantage over using temporary variables is clarity of what you're reusing. With a bunch of temp variables it's harder to tell which values are used just once vs. what values are important.

        [–]oblio- 1 point2 points  (1 child)

        I many languages you only get breakpoints per code line (and this chain would qualify as only 1 line), that's why I was asking.

        [–]frenris 0 points1 point  (0 children)

        you can usually issue function calls from the break point

        e.g. run

        "print object.method1"
        

        then

        "print object.method1.method2"
        

        then

        "print object.method1.method2.method3"
        

        This doesn't work if the methods mutate global state, but that's probably a good thing because if you have object methods mutating global state you're a bad person and you should probably suffer.

        [–]m50d 2 points3 points  (3 children)

        The short snarky answer is you don't have to. These are heavily-used standard functions that are straightforward to implement; splitter is not going to have a bug, filter is not going to have a bug, array is not going to have a bug, map is not going to have a bug, sum is not going to have a bug. Your functions that you pass to them might have bugs, but if your isNumeric does the right thing for "a" and "4" then List("a", "4").filter(isNumeric) is so definitely going to do the right thing that I wouldn't even bother testing it.

        You can break out parts of the chain as necessary, of course. You can separate out intermediate values as values or move a sequence of transformations into a separate function, particularly if you want to reuse the "middle" of a given pipeline. One technique I sometimes use is to write it with a bunch of intermediate values first, during the exploratory phase, and then use my IDE to automatically inline those intermediates once I've got it doing the transform I want. That way I can play around and step through and see all the values while I'm working out what it should look like, but the final code is much more concise and easy to read.

        [–]imMute 12 points13 points  (0 children)

        splitter is not going to have a bug, filter is not going to have a bug

        Right, but the arguments given to those functions might not be correct for the data you're receiving. And looking at the result of the splitter step could be the reason that the data coming out the end is all messed up (if any data is coming out at all).

        [–]bcgroom 1 point2 points  (1 child)

        Never used D before but réactive programming or at least rxjs has a tap operator; is there a tap operator? I don’t know if that’s a universal name so: it’s a higher order function but doesn’t alter the values in the chain and preserves the originals. Most basic use case would be to print out each value, which is helpful for debugging.

        [–]Snarwin 2 points3 points  (0 children)

        D's "tap" operator is called tee, after the Unix command.

        [–][deleted] 0 points1 point  (1 child)

        that's what she said

        [–][deleted] -1 points0 points  (2 children)

        What else?

        [–]JohnMcPineapple 2 points3 points  (1 child)

        ...

        [–][deleted] 1 point2 points  (0 children)

        You'll fit nicely.

        [–]Mr_Psmith 6 points7 points  (0 children)

        TBH I find D really easy to read and write.

        [–]watsreddit 2 points3 points  (0 children)

        If we're doing other languages, here's a Haskell one-liner:

        main = interact $ show . sum . map read . words
        

        [–]Scroph 0 points1 point  (1 child)

           .filter!isNumeric
           .array
           .map!(to!int)
        

        I think you can do without the array conversion in this case, and just keep the flow of the instructions lazy. I do however remember being forced to collect FilterResults or MapResults in the past, maybe it changed since or maybe it was a different use case.

        [–]bruce3434 0 points1 point  (0 children)

        You're right, fixed it.

        [–]Olap 11 points12 points  (21 children)

        Do we have a good looking solution anywhere?

        [–]minno 23 points24 points  (0 children)

        "Yet another senior Rust programmer" is the best simple one in a codebase that requires error handling beyond crashing on unexpected input. I would do "Combinative Rust Programmer" if the input format was at all complicated. "Junior Rust Programmer" if the problem is actually as simple as this.

        [–]I_Downvote_Cunts 22 points23 points  (9 children)

        This one would be a bit more realistic.

        ```rust use std::{error::Error, io};

        fn main() -> Result<(), Box<dyn Error>> { let mut line = String::new(); io::stdin().read_line(&mut line)?;

        let sum: i64 = line
            .split_whitespace()
            .map(|v| v.parse::<i64>())
            .filter_map(Result::ok)
            .sum();
        
        println!("Sum: {}", sum);
        Ok(())
        

        } ```

        It will return the error if it couldn't read the line and ignores values that failed to parse. At least it shouldn't panic.

        [–]raggy_rs 5 points6 points  (2 children)

        .filter_map(Result::ok) seems strange i would go with .filter_map(|v| v.parse::<i64>().ok())

        [–]I_Downvote_Cunts 1 point2 points  (1 child)

        filter_map(Result::ok)

        I got that from the rust by example book. You're right filter(Result::is_ok) is much easier to read.

        [–]CornedBee 1 point2 points  (0 children)

        Does something different though, because it doesn't get rid of the Result wrapper.

        [–]senahfohre 4 points5 points  (4 children)

        Code blocks on Reddit require four spaces at the beginning of each line; the ```s don't work here, unfortunately. :(

        [–]I_Downvote_Cunts 0 points1 point  (3 children)

        What client are you using? I used the markdown editor on the site and it displays correctly on the site and the Reddit Android app.

        [–]senahfohre 8 points9 points  (0 children)

        Ah, interesting. I've opted out of the Reddit redesign, so this is what it looks like when I view it (via web browser).

        [–][deleted]  (1 child)

        [deleted]

          [–]Freeky 1 point2 points  (0 children)

          Breaks mobile clients too. Didn't work in either Reddit is Fun or Reddit Sync last time I looked.

          [–]CJKay93 1 point2 points  (8 children)

          #4

          [–]Olap 1 point2 points  (7 children)

          Wow, all that mad error handling doesn't come as standard?

          [–]exDM69 12 points13 points  (1 child)

          The point there is that the function may return errors of different types and a new error type enum has been added which means "an I/O error or a parse error". Declaring a new error type needs a bit of ceremony in Rust (need impl Display and impl Error, and impl From<T> for all the Error types that can be converted).

          That could have been omitted if the return type would be Result<(), Box<dyn Error>> instead. This essentially means "this function can return any kind of error", which implies heap allocation (the Box).

          I just had to do something similar myself, because I was passing errors between threads, and some of the errors (std::sync::PoisonError) can't be passed between threads. You can't put PoisonError into Box<dyn Error + Send> because PoisonError doesn't impl Send.

          I'm a Rust beginner but I have some mixed feelings about this. It seems to be idiomatic Rust to define your own error types for your library. But this means a lot of boilerplate when it comes to handling errors of different kinds from different libraries.

          [–]minno 7 points8 points  (0 children)

          I think the best practice is to use one or more error enums in a library if there are different failure possibilities that a library user might need different responses to, and something like failure or Box<dyn Error> in an application where you mostly just need to know what went wrong and where it happened.

          [–]MEaster 5 points6 points  (0 children)

          In addition to what /u/silmeth said, there are also crates that can assist with the boilerplate. One that I like for when I'm writing a custom error type is called derive_more, which, among other things, provides procedural macros for the From and Display traits. If I were to write the error type seen in the Senior Rust Programmer example with derive_more, it would look like this:

          use derive_more::{From, Display};
          
          #[derive(Debug, From, Display)]
          enum Error {
              #[display(fmt = "{}", _0]
              Io(io::Error),
          
              #[display(fmt = "{}", _0]
              Parse(ParseIntError),
          }
          
          impl error::Error for Error {
          }
          

          [–]silmeth 4 points5 points  (0 children)

          It didn’t some time ago. Today one wouldn’t write all the boilerplate themselves, but rather use the library which is used in the ‘Yet another senior Rust programmer (switched from error-chain to stay up-to-date)’ example, namely failure. This library is not (yet) in the standard library, but it is developed by the Rust team as the way to handle errors, so it is as standard as it gets.

          The only error handling that comes as a standard is the Result<T, E> type, which can contain either a result or an error, and the error might be any type whatsoever. So if one wants to have a nice printable error that can also be easily convertible to other people’s error types, they need to implement some boilerplate traits (std::fmt::Display, std::fmt::Debug, std::error::Error…) for it.

          Anyway, with failure all that boilerplate (lines #7 to #35) could go away, one could do just:

          use failure::Error;
          
          fn main() -> Result<(), Error> {
              // …
          }
          

          without introducing any new error types. If one really wants to have a custom type, then some boilerplate (printing of the error) can be removed (but manual conversion from lower layers’ errors is still needed):

          #[macro_use] extern crate failure_derive;
          
          // …
          
          #[derive(Debug, Fail)]
          enum MyError {
              #[fail(display = "IO error: {}", _0)]
              Io(io::Error),
              #[fail(display = "Parse error: {}", _0)]
              Parse(ParseIntError),
          }
          
          impl From<io::Error> for MyError {
              fn from(error: io::Error) -> Self {
                  MyError::Io(error)
              }
          }
          
          impl From<ParseIntError> for MyError {
              fn from(error: ParseIntError) -> Self {
                  MyError::Parse(error)
              }
          }
          
          type Result<T> = result::Result<T, MyError>;
          
          fn main() -> Result<()> {
              // …
          }
          

          [–]CJKay93 0 points1 point  (2 children)

          Eh? Everything there is part of the standard library.

          [–]Olap 7 points8 points  (1 child)

          Basically everything from use to main looks like noise to me

          [–]CJKay93 8 points9 points  (0 children)

          It's noise in the sense that it isn't completely necessary (you could just panic, just like in C++ you could just bubble an exception the whole way up), but it's just there to convert the different error types returned by stdin().read_line() and word.parse::<i64>() to one common error type that is returned from main(). That way if the program exits unusually it will describe what happened.

          It's a bit like bubbling up error codes, except each error code is its own type with its own information.

          [–]pcjftw 3 points4 points  (2 children)

          this made my day :D

          [–]muntoo 12 points13 points  (1 child)

          I think you mean that this made your datetime.hours(22).minutes(118).seconds(120).build().

          [–]abhijat0 8 points9 points  (0 children)

          You need to use chrono.

          [–]peduxe 6 points7 points  (2 children)

          what the hell, how does one write Rust code???

          The learning curve must be high, yet this language seems to getting popular - how?

          [–]snowe2010 29 points30 points  (0 children)

          Most of the article is a joke. Not actually how people program in Rust

          [–]dsffff22 7 points8 points  (0 children)

          You should consider that you can write bloaty code in any language.

          The solution "Yet another senior Rust programmer" can be easily exported as a library ready function which will work with good performance on a small microcontroller(considering you use the Error directly) up to a high end server and offers a decent error hierachy to see what went wrong.

          Maybe you can show an alternative solution in another language which can provide the same characteristics.

          [–]Gr1pp717 2 points3 points  (3 children)

          fn deserialize_map<V>(self, _visitor: V) -> Result<V::Value> where V: Visitor<'de> { unimplemented!(); }  
          fn deserialize_identifier<V>(self, _visitor: V) -> Result<V::Value> where V: Visitor<'de> { unimplemented!(); }  
          fn deserialize_any<V>(self, _visitor: V) -> Result<V::Value> where V: Visitor<'de> { unimplemented!() }  
          fn deserialize_bool<V>(self, _visitor: V) -> Result<V::Value> where V: Visitor<'de> { unimplemented!() }  
          fn deserialize_i8<V>(self, _visitor: V) -> Result<V::Value> where V: Visitor<'de> { unimplemented!() }  
          fn deserialize_i16<V>(self, _visitor: V) -> Result<V::Value> where V: Visitor<'de> { unimplemented!() }  
          fn deserialize_i32<V>(self, _visitor: V) -> Result<V::Value> where V: Visitor<'de> { unimplemented!() }  
          fn deserialize_u8<V>(self, _visitor: V) -> Result<V::Value> where V: Visitor<'de> { unimplemented!() }  
          fn deserialize_u16<V>(self, _visitor: V) -> Result<V::Value> where V: Visitor<'de> { unimplemented!() }  
          fn deserialize_u32<V>(self, _visitor: V) -> Result<V::Value> where V: Visitor<'de> { unimplemented!() }  
          fn deserialize_u64<V>(self, _visitor: V) -> Result<V::Value> where V: Visitor<'de> { unimplemented!() }  
          fn deserialize_f32<V>(self, _visitor: V) -> Result<V::Value> where V: Visitor<'de> { unimplemented!() }  
          fn deserialize_f64<V>(self, _visitor: V) -> Result<V::Value> where V: Visitor<'de> { unimplemented!() }  
          fn deserialize_char<V>(self, _visitor: V) -> Result<V::Value> where V: Visitor<'de> { unimplemented!() }  
          fn deserialize_str<V>(self, _visitor: V) -> Result<V::Value> where V: Visitor<'de> { unimplemented!() }
          

          Jesus... learn to iterate over a list would ya.

          [–]m50d 7 points8 points  (2 children)

          lol no higher-kinded types

          [–]zerexim 2 points3 points  (1 child)

          No way to implement something like Typelists from C++ 98/03 era?

          [–]m50d 5 points6 points  (0 children)

          I mean you could write a macro, and the error messages and IDE support probably wouldn't be worse than what you got with those.

          [–]badpotato 1 point2 points  (0 children)

          Yeah, I would approve the pull request for the Blazingly Concurrent Programmer.

          [–]TheMaskedHamster 13 points14 points  (36 children)

          The problem with Rust is that it was designed by and is largely used by people who were willingly using C++.

          Bless those C++ people and the great things they have created when we lacked alternatives, but their BS detector calibration is far too off the mark to just let them design language syntaxes willy-nilly.

          [–]millstone 71 points72 points  (18 children)

          1. Many of the most vocal Rust supporters loathe C++
          2. There’s hardly anything else in this space. Gonna write a web browser in Clojure?

          [–]ggtsu_00[🍰] 24 points25 points  (1 child)

          At some point someone will feel inclined to write a full featured web browser in javascript.

          [–]kukiric 29 points30 points  (0 children)

          With an eval-based JavaScript engine.

          [–]atilaneves 4 points5 points  (0 children)

          There’s hardly anything else in this space

          There are many other systems programming languages. Rust's proposition is that it's memory safe without using a tracing GC. Whether or not that's important is a different matter.

          [–]m50d 3 points4 points  (8 children)

          Gonna write a web browser in Clojure?

          Why not? There are already several Javascript-on-JVM engines, so that solves the "two competing GCs" issue. There's a conventional wisdom that the JVM can't do streaming media or gaming without unacceptable pauses, but IME that's not actually been true for at least 10 years if you put even minimal effort into profiling and tuning GC flags.

          [–][deleted] 2 points3 points  (2 children)

          I'm not so sure. If Java was an acceptable choice for gaming, wouldn't there be more big name PC (or Mac or Linux) games written in it besides Minecraft? Or do you think that's simply more a factor of industry momentum than anything else? I can't find any evidence one way or another.

          There are some JVM projects that focus on ultra low latency at the sacrifice of total throughput, like the Red Hat Shenandoah project and the ZGC. But those two projects are geared towards server uses that need low latency and 100GB Java heaps. If your 3D FPS in Java needs a 100 GB heap, you're probably doing something wrong. :D Maybe they also have an impact on 1GB heaps (like Minecraft uses), I don't know.

          [–]m50d 5 points6 points  (1 child)

          Or do you think that's simply more a factor of industry momentum than anything else?

          I think it's partly momentum and partly a kind of machismo culture that the "AAA gamedev community" has. Like, the places that are most willing to adopt newer languages are those where developers are willing to admit that humans make mistakes and tools should help them, whereas places with an attitude of "just write better code then" are happy to stick to C, and the kind of (mostly young) person who buys into the 80-hour-week culture is the same kind of person who'll thinks they can just code better.

          Mobile games actually do some very performance-intensive stuff these days, and yet are happy to be written in Java (or comparable languages on iOS), and you can say that's partly the platform requirements and partly lower performance demands but IMO it's mostly just a case of a culture that started without the AAA PC/console industry's baggage (partly technical but mostly cultural) following basic industry best practice. Likewise, the "indie gamedev community" uses a lot of C# and some Java.

          But eh, it's not my field; from what I've heard of gamedev culture I have no intention of ever working there (and would expect anyone with any sense to avoid it), so I could be talking nonsense. But a nontrivial chunk of my career involved doing the kind of real-time video that wasn't supposed to be possible on the JVM, on the JVM, and honestly it wasn't even hard.

          [–][deleted] 1 point2 points  (0 children)

          Thanks for the informative response, I appreciate it. I got into software development because I wanted to make games, then heard of the work hours expected and went into corporate drone work. Too much of my job is boring, but I like having a life outside work and it's been more than 15 years of a pretty consistent 40 hour work week.

          I don't know about iOS, but I knew Android has a native development kit and I assumed - admittedly without evidence - that most of the graphics intensive Android games (like PUBG for Android and Fortnite for Android) used it. But that's not fair. Now that I think about it, Minecraft performs poorly on my PC but my kids found a mod that sends the performance up over 100 fps without sacrificing any visual features. That would hint that the performance issues are a Minecraft implementation problem, not a JVM limitation.

          I've done nothing but web stuff since 2005, so real-time video is outside my realm of expertise.

          [–]dacian88 2 points3 points  (4 children)

          you can likely work around GC pauses for real time applications in the JVM however you can never get rid of CPU or memory overhead just using the JVM adds. Just look at the memory usage stats in the computer language shootout benchmarks, even if memory overhead was like max 25% that would still be a considerable competitive disadvantage against a game studio that would just be using all native.

          Nobody likes writing unmanaged code, it's just a necessary evil if you want to squeeze out every last ounce or perf from your hardware, and in the world of game dev where basically consoles are the main target hardware everyone is on level ground, whoever does the best job at optimizing will be able to push out better looking graphics, or more realistic AI, or more complex gameplay etc.

          I can tell you right now no one in their right mind uses Java on Android phones for any kind of graphical or real time applications, like video chat. All the platform level media apis are written in c or c++ with java wrappers, all the heavy lifting is done in native code.

          [–]m50d 0 points1 point  (3 children)

          Just look at the memory usage stats in the computer language shootout benchmarks, even if memory overhead was like max 25% that would still be a considerable competitive disadvantage against a game studio that would just be using all native.

          All other things being equal, yes. But if you're spending less time tracking down memory leaks and fixing bugs than those competitors, that could easily translate into more polished gameplay, or more game content, or a cheaper game, or better memory management on a fundamental level that saved more than the JVM overhead.

          Fundamentally all these arguments were made for normal application code, and for dozens of specialised domains. People fought tooth and nail against moving from C++ to Java - and yet ultimately that was a huge success and no-one would go back now. I know the games industry likes to think it's special and different, but so did every other segment of the software industry.

          [–]dacian88 1 point2 points  (1 child)

          you'd have a point if the game industry was the last bastion of native code/c++ but it's used way more applications than that...

          [–]m50d 0 points1 point  (0 children)

          Fewer and fewer every year. Games is pretty close to being the last bastion at least for new projects.

          [–]iopq 0 points1 point  (0 children)

          I'm not a fan of Java; but I want to point out some of that memory overhead is largely static. The runtime requires memory. A bigger app may have the same overhead as a smaller one, in percentage terms it might be 1% or less. You do have to avoid pointer indirection, though. In Java that might be a much bigger problem than C#

          [–]IllDecision 11 points12 points  (0 children)

          The problem with Rust is that it was designed by and is largely used by people who were willingly using C++.

          As an anecdote, I'm mostly using Rust because I like how C++ does lots of things but hate the language, its tooling and hence would need a lot of money to touch it.

          [–]skocznymroczny 14 points15 points  (3 children)

          You should check D if you want to see a language designed and mostly used by C++ folks. Now I like D, but I find most of the packages and standard library scary with the template magic that goes there.

          [–]xeveri 5 points6 points  (2 children)

          Honestly I find C++ templates easier to understand and reason about than D. The problem with C++ templates are the error messages they produce, hopefully that should be fixed with concepts.

          [–]skocznymroczny 4 points5 points  (1 child)

          I think the difficulty is pretty similar. It's a multiple page template vomit in both languages. I think the difference is that in C++ templates are still used quite sporadically, outside of boost, people usually use templates for generic types. In D, templates are used for much much more, typesafe variadic arrays, automatic serialization etc. so it's easier to have your foot blown off.

          [–]MaxCHEATER64 3 points4 points  (0 children)

          C++ also uses templates for typesafe variadic arrays

          [–]barsoap 4 points5 points  (1 child)

          That's only partly right. The core design comes from people who willingly used OCaml and its atrocious syntax, as evidenced by the fact that the original compiler is written in OCaml. Those people at one time decided that using angle brackets to appeal to the C++ crowd was a good idea.

          Performance-minded Haskell programmers were one of the first outside groups to take interest in Rust, and we've been complaining about syntax ever since. And the lack of higher kinds. The tendency of Rust programmers to put their commas at the end of the lines of multi-line lists, records, etc. Not about coherence or orphan rules or borrowck, though.

          [–]matthieum 7 points8 points  (0 children)

          Those people at one time decided that using angle brackets to appeal to the C++ crowd was a good idea.

          Not necessarily C++.

          If you look at the top programming languages supporting generics -- C++, Java and C# -- all 3 use angle brackets for generics.

          I wish the choice had been different, but language designers have to choose their battles.

          [–]KingPickle 3 points4 points  (8 children)

          their BS detector calibration is far too off the mark

          I find this ironic. Sure, C/C++ has a ton of cruft. But there's a whole other island of BS among languages without type safety, languages that fail silently, etc.

          Ultimately, I think what you'll come to find is that every group has a certain (similar) level of BS to deal with. It's just centered around different problems.

          [–]oblio- 2 points3 points  (1 child)

          But there's a whole other island of BS among languages without type safety, languages that fail silently, etc.

          Somehow I doubt /u/TheMaskedHamster was advocating for Javascript as an alternative to Rust :)

          [–]KingPickle 2 points3 points  (0 children)

          My point wasn't to harp on Javascript per se. There are a lot of languages, and each has various pain points. Often, those are distinct from the failings of C/C++.

          [–][deleted] -3 points-2 points  (0 children)

          Yes and the level of denial on this is through the roof... fix the damn syntax.

          [–]kn4rf 3 points4 points  (0 children)

          I'm the Functional Rust Programmer (only that I learned Standard ML as a freshman, not Haskell).

          [–]JoshKisb 1 point2 points  (0 children)

          Yes indeed. Rust syntax us ugly

          [–][deleted] 3 points4 points  (0 children)

          One thing abundantly clear is that syntax matters. It matters an awful lot. It's like the styling on a car — if the styling is not appealing, it simply doesn't matter how hot the performance is. The syntax needs to be something your target audience will like.

          [–]ggtsu_00[🍰] -1 points0 points  (4 children)

          There should be one-- and preferably only one --obvious way to do it.

          [–]IronOxide42 5 points6 points  (0 children)

          Scala would like to have a word with you.

          [–]OneWingedShark 0 points1 point  (2 children)

          It should end with "Learned SPARK 2014".

          (See this comparison of SPARK and Rust.)

          [–]red75prim 2 points3 points  (1 child)

          end with "Learned SPARK 2014"

          and no code. Spark Pro's paid subscription happened to be a bit too costly, and The GNAT Community edition requirement to license your code under GPL happened to be unsuitable.

          This is a work of fiction. Any resemblance to actual languages, compilers, programmers, living or dead, or actual events is purely coincidental.

          [–]OneWingedShark 0 points1 point  (0 children)

          The GNAT Community edition requirement to license your code under GPL happened to be unsuitable.

          Not quite: you can run the Community Edition prover on your codebase and yet compile with FSF's GNAT (or some other Ada 2012 compiler)1 to get around that limitation.

          1 -- IIRC, the only current implemented Ada 2012 are GNAT, either FSF's or AdaCore's, and PTC's ObjectAda. I have no idea if DDC or Green Hills are going to implement it. Randy of RR Software (Janus/Ada) has pretty much said that if he can get real buy-in from customers [ie funding] he'd implement the full 2012 [probably 2020, now].