Slide with text: “Rust teams at Google are as productive as ones using Go, and more than twice as productive as teams using C++.”

In small print it says the data is collected over 2022 and 2023.

  • @orclev@lemmy.world
    link
    fedilink
    38 months ago

    Probably because Rust doesn’t have exceptions

    Well, it has something semantically equivalent while being more explicit, which is Result (just like Option is the semantic equivalent of null).

    and I’m pretty sure there are no guarantees with panic!().

    I actually do quite a bit of bare metal Rust work so I’m pretty familiar with this. There are sort of guarantees with panic. You can customize the panic behavior with a panic_handler function, and you can also somewhat control stack unwinding during a panic using std::panic::catch_unwind. The later requires that anything returned from it implement the UnwindSafe trait which is sort of like a combination Send + Sync. That said, Rust very much does not want you to regularly rely on stack unwinding. Anything that’s possible to recover from should use Result rather than panic!() to signal a failure state.

    • Yup. My point is just that scope(failure) could be problematic because of the way Rust works with error handling.

      What could maybe be cool is D’s in/out contracts (example pulled from here):

      int fun(ref int a, int b)
      in
      {
          assert(a > 0);
          assert(b >= 0, "b cannot be negative!");
      }
      out (r)
      {
          assert(r > 0, "return must be positive");
          assert(a != 0);
      }
      do
      {
          // function body
      }
      

      The scope(failure) could partially be solved with the out contract. I also don’t use this (I find it verbose and distracting), but maybe that line of thinking could be an interesting way to generically handle errors.

      • @orclev@lemmy.world
        link
        fedilink
        English
        28 months ago

        Hmm… I think the Rust-y answer to that problem is the same as the Haskell-y answer, “Use the Types!”. I.E. in the example above instead of returning an i32 you’d return a NonZero<u32>, and your args would be a: &NonZero<u32>, b: u32. Basically make invalid state unrepresentable and then you don’t need to worry about the API being used wrong.

        • I’m more referring to a more general application, such as:

          fn do_stuff() -> Result<...> {
              if condition {
                  return Error(...)
              }
          
              return Ok(...)
          } out (r) {
              if r.is_err() {
                  // special cleanup (maybe has access to fn scope vars)
              }
          }
          

          That gives you some of the scope(failure) behavior, without as many footguns. Basically, it would desugar to:

          fn do_stuff() -> Result<...> {
              let ret = if condition { Error(...) } else { Ok(eee) };
          
              if ret.is_err() {
                  ...
              }
          

          I’m not proposing this syntax, just suggesting that something along these lines may be interesting.

          • @orclev@lemmy.world
            link
            fedilink
            English
            2
            edit-2
            8 months ago

            I think the issue with that is that it’s a little bit of a solution in search of a problem. Your example of:

            fn do_stuff() -> Result<...> {
                if condition {
                    return Error(...)
                }
            
                return Ok(...)
            } out (r) {
                if r.is_err() {
                    // special cleanup (maybe has access to fn scope vars)
                }
            }
            

            isn’t really superior in any meaningful way (and is arguably worse in some ways) to:

            fn do_stuff() -> Result<...> {
                if condition {
                    // special cleanup (maybe has access to fn scope vars)
                    return Error(...)
                }
            
                return Ok(...)
            }
            

            For more complicated error handling the various functions on Result probably have all the bases covered.

            For what it’s worth a lot of my day to day professional work is actually in Java and our code base has adopted various practices inspired by Rust and Haskell. We completely eliminated null from our code and use Optional everywhere and use a compile time static analysis tool to validate that. As for exception handling, we’re using the Reactor framework which provides a type very similar to Result, and we essentially never directly throw or catch exceptions any more, it’s all handled with the functions Reactor provides for error handling.

            I just don’t think the potential footguns introduced by null and exceptions are worth it, the safer type level abstractions of Option and Result are essentially superior to them in every way.

            • @sugar_in_your_tea@sh.itjust.works
              link
              fedilink
              3
              edit-2
              8 months ago

              We completely eliminated null from our code

              Nice. We use Python and use None everywhere. I ran pyright on our codebase, and while we use typing religiously, our largest microservice has ~6k typing errors, most of which are unchecked Nones. We also use exceptions quite a bit, which sucks (one thing really annoys me is a function like check_permissions() which returns nothing, and throws if there’s an issue, but it could totally just return a bool. We have nonsense like that everywhere.

              I use Rust for all of my personal projects and love not having to deal with null everywhere. I’d push harder for it at work if others were interested, but I’m the only one who seems passionate about it (about 2-3 are “interested,” but haven’t even done the tutorial).

              • @orclev@lemmy.world
                link
                fedilink
                2
                edit-2
                8 months ago

                Yeah as far as I’m concerned null is public enemy number one. I refuse to work in any language that doesn’t allow me to indicate in some fashion that a variable is non-nullable. I just about had an aneurysm when I found out that JavaScript not only has null, but also nil and undefined and they all mean something subtly different. To be fair though, JavaScript is like a greatest hits of bad language design.

                  • @orclev@lemmy.world
                    link
                    fedilink
                    English
                    28 months ago

                    It sort of has nil. While a type can be null or undefined when evaluated, nil is used in many of the JS libraries and frameworks to mean something that is either null or undefined. So you’ll see functions like function isNil(value) { return value == null || value == undefined } and they’ll sometimes often confuse things even more be actually defining a nil value that’s just an alias for null which is just pointlessly confusing.

                    As an aside, basically every language under the sun has NaN as it’s part of the IEEE floating point standard. JavaScript just confuses the situation more than most because it’s weakly typed so it doesn’t differentiate between integers, floats, or some other type like an array, string, or object. Hence anything in JS can be a NaN even though it really only has meaning for a floating point value.