1. 60
  1. 15

    The high-assurance thing is a big problem, yes, and I’ve gone down the same route (lints to prevent panics in my code, trying things like no_panic crate, catch_unwind in key places and pay a frustrating performane cost, panic injection tests to test the latter, manual auditing… I still can’t be sure I caught everything).

    Of course, C++ team I knew used to at their weekly meeting (and probably still do, ten years later) have QA summarize what they learned from core dumps in production. So could be worse!

    1. 8

      An example of this for me, is there’s a nasty Firefox crash that has been happening, due to some very deep part of the font stack having a panic (instead of using Results), so what could have been an error message about a font not being loadable (and having the caller deal with that), all of FF falls over!

      Results can be annoying, but honestly now that we have ? and result conversion, I think it’s pretty frustrating to have this situation. Of course this is sometimes replacing what would sometimes be a segfault later, but there are tools to handle this, and you can just change the API signature and work through the errors!

      1. 6

        Is it possible that high-assurance kinda wants a different, less-ergonomic programming language with proofs all over it, so you can avoid things like infinite loops too?

        1. 3

          This feels more like a mindset problem than a language problem. LLVM is written in C++ and has a habit of using asserts in a bunch of places where it really should be propagating an error properly. C++ in general, and LLVM in particular, have a set of flexible mechanisms for reporting a recoverable error but this kind of thing shows up in places where people don’t want to think through what recovering should look like. For example, if the IR is not internally consistent, you’d need to propagate the error out to the caller of LLVM libraries and tell them to discard the entire function or module. In most command-line tools, crashing and printing a stack trace is a close approximation of what the tool would do. In a web service or a GUI app, this isn’t acceptable.

        2. 11

          I’ve been learning Rust the past couple weeks strictly for hobbyist reasons. I’m coming from the dynamic typing world of Python and JavaScript. While I’m familiar with the strongly typed paradigms of C and Java, I figured spending my time in Rust would be more beneficial.

          There are a lot of individual things I like in Rust, but programming in it in general feels like I’m trying to learn a new sport and I can’t quite seems to get my sense of balance. E.G., “I’ve been out on the water a hundred times now, so why can’t I comfortably stand on the surfboard now?” type of feeling. If that makes sense.

          I’ll keep plowing forward with it though. I’ve got The Rust Programming Language book from No Starch Press, and I have been spending a lot of my time in the Rust Book online. I’ll find my balance, I just wish I could find it sooner than later.

          1. 13

            While I’m familiar with the strongly typed paradigms of C and Java

            C is barely type at all, and Java is a weird hybrid of a poor type system and just being dynamically typed anyway. I wouldn’t call either “strongly typed”.

            1. 4

              C is barely type at all, and Java is a weird hybrid of a poor type system and just being dynamically typed anyway. I wouldn’t call either “strongly typed”.

              Maybe I should have chosen “static” instead, as I also referred to “dynamic” typing rather than “weak” typing. Or maybe type safety would have been even a better comparison? Shrug.

              1. 22

                I wouldn’t worry too much, internet fights about what counts as “static” or “strong” or “typed” are generally not worth your time.

                1. 9

                  The key here is that there is no single definition of “strong” or “static” types. It is a spectrum. For example, Rust has algebraic data types, C++ does not. They are both static and strong in the broad sense, but algebraic data types mean that you can express more things at the type level.

                  It’s not like saying “I’m familiar with C, so I know everything that can be done with static types,” is accurate, I think is the point of this response.

                  1. 2

                    It’s not like saying “I’m familiar with C, so I know everything that can be done with static types,” is accurate, I think is the point of this response.

                    Indeed.

              2. 6

                I get what you’re saying and if I may suggest two things which have helped me pick up rust: writing a small but complete CLI application and watching live or recorded streams (for instance Ryan Levick or Jon Gjengset are formidable tutors)

                1. 4

                  Maybe you should listen to your gut. Rust is a relatively new language, it is not clear it will stand the test of time.

                  1. 8

                    That’s fair. However, even though I’m struggling to find my balance, I find learning it exciting. I think I’d rather learn as much of the ins and outs as I can and walk away with an informed opinion than bail early. At least I’m having fun, even if I keep falling off the surfboard.

                    1. 6

                      I hope you stick with it. It is a relatively hard-to-master language, IMHO. Java, or an GC language doesn’t force you to think about ownership the way Rust puts that front and center. The ownership model has a dramatic effect on the kind of datastructures you can and should design. And it effects the way in which you use and share data across the entire application.

                      I believe the tradeoff is worth it, because you can catch certain classes of memory safety bugs at compile time, which can lurk undetected for years in other codebases. These are the kind of bugs that cause big problems later.

                      1. 2

                        I hope you stick with it.

                        I plan on it.

                        It is a relatively hard-to-master language, IMHO. … The ownership model has a dramatic effect on the kind of datastructures you can and should design. And it effects the way in which you use and share data across the entire application.

                        This might be the balance I’m struggling to find. A lot of my compiler errors are related to owneship and I keep having to come back to the docs to remind myself how it works.

                  2. 2

                    I made a similar jump. It informed how I learned TypeScript and then made a whole lot more sense once I learned Haskell. Some tidbits that would’ve helped me follow.

                    Static typing, especially in something like TypeScript, can be thought of as an elaborate linter. At times it can be restrictive, but surprisingly often it’s leading you towards better code.

                    Strong static typing as in Rust’s case is like static typing but where your types are accurate to what will be there at runtime, so you’re probably going to need to do some validation or casting at your application boundaries. In Rust’s case also there’s no duck typing, you can’t just carry around data the compiler doesn’t know about at compile-time.

                    Traits are interfaces for shared abstractions across types. I really struggled with this until I saw typeclasses in Haskell. I think learning something relatively simple there like Functor can be instructive. It’s not entirely unlike extending prototypes in JavaScript, but here the compiler can use that information at compile-time to let you write functions which constrain input to support certain things, like for example an Eq constraint would mean “I’ll accept any input for which I can test equivalence”.

                    1. 1

                      There is always duck typing if you want it, in any language. The best way in rust IMO is trait objects, but you can even get Java-style dynamic typing if you want (with possible a single use of unsafe in the library providing it).

                  3. 9

                    All good points (that I largely agree with), though for me the biggest pain point remains async. Mentioning this feels like beating a dead horse by now (and I really appreciate that the async story is actively being worked on), but async Rust still feels ends up looking too often like an inscrutable mess.

                    I usually try to avoid async at all costs, which is unfortunately not possible in an environment without threads like wasm (another area where Rust feels extremely unidiomatic, but to be fair the wasm story is not part of the official language in the same way that async/await are).

                    1. 6

                      I don’t share that sentiment at all. I use Rust’s async for several projects, including some quite complex and high-traffic, and async works great.

                      In many aspects the Rust async ecosystem is now more mature and reliable than Node’s.

                      1. 3

                        I agree that the ecosystem is quite solid, but the language integration is still rough around the edges. For example, there is no async recursion or async traits out of the box and lifetimes sometimes get considerably more tricky in the presence of async. I haven’t used async in Node, but at least in Dart async feels much more first-class than in Rust.

                        Is that Rust’s “fault”? No, not really, because it is hard to see how some of these issues could be resolved without introducing costly abstractions. Of course it is easier to just box some things in JS or Dart than in Rust where the principle of zero-cost abstractions is taken quite seriously. But while porting something from sync to async in e.g. Dart is quite painless, going from sync to async in Rust introduces a lot of additional complexity. Is that complexity worth it? Often yes, and I personally work a lot more with async/await in Rust than with threads. But I try to think much more carefully about whether it is worth it to pay this additional complexity cost in Rust than in other languages and I believe that choosing sync code as a default is a sensible choice.

                    2. 6

                      Preventing all runtime panics seems like an impossible task. Your program can always run out of memory.

                      Given that programs therefore need to handle unrecoverable situations anyway, it doesn’t seem that bad to me to allow judicious use of panics by developers to signal “I’ve reached an unrecoverable state” or “the consuming developer has made a mistake here”, especially since panics can be caught.

                      Two other articles I’ve read before with interesting perspectives on this topic:

                      • Error Handling in Node.js, especially the section on operational vs. programmer errors. It’s interesting how focused on reliability and debuggability the original Joyent-centric Node community was! (Bryan Cantrill has a great talk on this.)
                      • The Error Model in Midori, also discussing how bugs aren’t recoverable errors.
                      1. 8

                        I think that statement is right when it comes to programs that run on general purpose operating systems like Windows and Linux. I think it’s less accurate when it comes to say firmware where you might have no multitasking, no operating system, no dynamic allocation and have rules disallowing recursion (mutual or self recursion) to enforce strictly-bounded stack usage.

                        1. 4

                          running out of memory is a good example of a tough error. But you can set up a program to reserve space for error handling (And yeah maybe that fails too, but if you have 0 memory and your program can’t even start up….)

                          I think that there’s a false dichotomy though. For example in Python I can write a web service, and have a wrapper around my views with a “catch-all” exception that will handle almost everything. It probaly won’t handle OOMs cleanly, but there are huge classes of “just give up” errors that get handled by this.

                          Of course having exceptions is a whole thing, and Result wins the ergonomic argument! But honestly having exceptions for “sorry, I give up here, please go up to some part of the stack and assume everything’s busted” is a very nice feature for not having your entire program fall over!

                          1. 1

                            And you can do this in Rust if you make a custom panic handler, but IMO it’s one of those things where before doing it you need to imagine your father sternly saying “Do you really need this? Really?”, just to make sure.

                        2. 3

                          That would explain why the author’s site runs on Ruby, not Rust…

                          1. 2

                            The lack of a good testing story is frustrating to me. It feels like there’s no real good equivalent of, say, pytest or jest.

                            1. 1

                              As a person who knows none of these in depth, what are the major advantages of pytest/jest over cargo test?

                              1. 1

                                pytest lets you define fixtures that other tests can use. A fixture runs some code before the test function is invoked, and it has some test code that runs afterwards, even if the test itself fails. You can also have it reuse the same fixture for all tests in a file or all tests in a session (if it’s something really expensive to setup/teardown).

                                You can run a test with a bunch of different values for a given input. You can do this in a loop in a Rust test, but then they only count as one test; pytest parameterized tests are fully separate.

                                You can also mark tests with user-defined markers and say “only run tests with this mark” or “run all tests that don’t have this mark”.

                            2. 2

                              Shouldn’t the subset of Rust developped for use in the linux kernel one day address the high assurance concerns ? WRT panics

                              1. 1

                                I really wish all rants were this informative, clear and spot-on.