Settings

Theme

Rust vs. Go: Battle for the Back End

effective-programmer.com

17 points by naveed125 9 months ago · 58 comments

Reader

DeathArrow 9 months ago

>the truth is when we needed super high performance in production, the answer was either Go or Rust.

For high performance, the answer is C, C++, Zig or Rust.

For backend it can be Go, it can be Java, it can be C#, it can be Python or Javascript.

C# is on par performance wise with Go and I suspect the same for Java.

Choosing a language for something has to be done by considering trade offs, benefits and disadvantages, on both long and short term.

Rust would fit system programming while Go will fit the backend. I know "the right tool for the job" is very often mentioned but I will mention it again.

For backend I would be interested in a functional language, be it F#, OCaml, Elixir or Clojure, because I grew tired of OOP, SOLID, design patterns, clean coding, kingdom of nouns and Uncle Bob. All things, I used long ago to believe in. But since no one is hiring functional programming and I don't have the resources to be the sole owner and developer of a startup, I am stuck in kingdom of nouns.

  • nesarkvechnep 9 months ago

    For backend almost certainly the winner is Elixir. It’s been slept on for years.

    • DeathArrow 9 months ago

      It might be a winner but who is going to employ you to write Elixir?

      • Gud 9 months ago

        I’ve been around long enough to see the underdog beat the well funded #1.

        Linux beat Windows, PostgreSQL beat MySQL.

        • derekperkins 9 months ago

          I'm not sure either of those examples is true. For servers, Linux won, but that's just a fraction of OS usage. MySQL still has much higher usage rates than Postgres, particularly at massive scale, so calling that "won" is premature at best

  • notpushkin 9 months ago

    Rust might make sense on the backend, though the ecosystem isn’t great. (I’m still waiting for something matching the experience of FastAPI in Python)

    • DeathArrow 9 months ago

      In the backend you wait for databases, caches, external APIs and so on. Rust wouldn't be a major performance win over any strongly typed, compiled, garbage collected language. But it would mean lower development speed in a world where time to market is important.

      If top raw performance was so important in the backend, people would have used C++ long before Rust became popular.

      Now you might have a rare use case where raw performance is important, but in that case you write that part in a low level language and the rest of the microservices in a more productive one.

go-nil-why 9 months ago

The fact that Go is considered a “modern” language and nil pointer exceptions are downright common blows my mind every day. Is there a static checker config that folks use to reduce this risk? Because coming from a TS background it’s legitimately bizarre to me that at $COMP insufficient nil pointer checking regularly causes major production outages.

  • hnlmorg 9 months ago

    Does it regularly cause major production outages? I’d love to see some data to back that claim because I’ve got Go services with uptimes of literally years.

    Also it might seem weird to you given Typescript is a completely different idiom and doesn’t even have pointers to begin with.

    That all said, I’m not going to defend the nil pointer problem in Go. It is something that can be largely mitigated with a few relatively small changes to the language. Though I couldn’t see them being entirely backwards compatible.

    • go-nil-why 9 months ago

      The issue isn’t the services that stay up forever, it’s the ones subject to a huge amount of churn where maintainers lose context on what is or isn’t possibly nil, and get loose with their checks.

      Edit, responded to pointer vs reference down thread.

      • hnlmorg 9 months ago

        The issue I’m addressing is your exaggerated verbiage which sounds like it’s a reasoned argument but is actually just biased nonsense.

        I’m not disagreeing that nil pointers are an ugly sore for Go. What I’m arguing against is your claim that Go services are constantly failing in production because of that sore point.

        The reason I make that argument is because:

        - sore points are a problem for every programming language. designing a programming language is a constant trade off of concerns. so there will always be stuff that doesnt work well. And I know this as an author of a programming language myself.

        - however if sore points prevent a language from being usable in a production context (assuming that’s its intended role), then that becomes an insurmountable problem.

        Nil pointers aren’t an insurmountable problem for Go. They’re ugly and arguably a terrible design choice, but not an insurmountable problem. Which is why Go succeeds despite this sore spot.

        Typescript has its own WTF moments too. Every language does.

        • go-nil-why 9 months ago

          Clearly, we don’t work at the same company.

          What I claimed applied to my company, exactly as I said.

          If it doesn’t apply to yours… ok? Congrats?

          Looking back, maybe you didn’t understand the shell variable syntax?

  • Mawr 9 months ago

    Assume all pointers are non-nil and use a generic Option type to represent values that may or may not exist:

        type Option[Value any] struct {
            value  Value
            exists bool
        }
        
        func (o Option[Value]) Get() (Value, bool) {
            return o.value, o.exists
        }
    
    So long as all such values use this type instead of abusing the nullability of pointers, you'll eliminate the problem of programmers forgetting to check for nil.

    It's not without its issues though, the ecosystem does not use such a type and you need a little bit of discipline to stick with it.

    But it's not that big of a deal if you're getting regular major outages...

    • go-nil-why 9 months ago

      Issue is that isn’t generated by protoc.

      Looking at the go ecosystem you’d think static nil traceability is some sort of Very Hard Problem…

  • phplovesong 9 months ago

    This applies to all languages with nil. The alternative being options (like ocaml).

    • BlackFly 9 months ago

      You're replying to someone who is claiming to come from typescript where nullability must be expressly opted into and once opted into must be branched upon without use of a monad (isomorphism notwithstanding). Kotlin would be another example of that. Even with python type hints the same thing can be achieved or with Java and annotations. The problems in all of these cases is the boundaries between the languages where the nullability or lack thereof is often just assumed and not asserted upon.

      There are language decisions other than options that can work, but they boil down to the same thing: force a branch before use. If go does not have this sort of secondary type system now, it won't be too long before someone sees the value.

    • nicoburns 9 months ago

      > This applies to all languages with nil. The alternative being options (like ocaml)

      It actually doesn't. Languages like Kotlin, C# and TypeScript special case null in the type system and allow you to specify whether or not a given type should include null or not.

    • josephg 9 months ago

      Yeah. But I don’t really consider any language which has implicit nullability to be “modern”. It’s a bad design. It causes bugs.

      Plenty of recent languages have fixed this problem - like zig, Swift, kotlin, rust and typescript. Go is behind the curve.

    • chris_overseas 9 months ago

      Not entirely true - e.g. Kotlin has null safety built into its type system, and I find it much nicer than the option/optional approaches used in various other languages.

      https://kotlinlang.org/docs/null-safety.html

      • josephg 9 months ago

        Looks similar to the syntax in Swift. I agree - it’s much nicer than all the mucking around you need to do in rust.

        It’ll never happen, but I wish rust added this kind of sugar on top of Option.

    • jahewson 9 months ago

      It's not the null that's the issue, it's the type system. As long as you have algebraic data types you can have safe null.

      Ocaml's `None | Some of int` has the same basic meaning as TypeScript's `number | undefined`

      • hnlmorg 9 months ago

        That’s not the same thing as a nil pointer though.

        Go data types cannot be null but a pointer to a data type can be. And what a nil pointers is, is a valid type of pointer but which points to an invalid point or memory.

        Typescript doesn’t have the same problem because it doesn’t have pointers.

        Rust somewhat this problem with its borrow checker but it’s still entirely possible to create a nil pointers in Rust and without using ‘unsafe’, but admittedly it’s not by writing idiomatic Rust.

        Go really should do more to prevent issues here but the real problem is around the creation of “objects” (since Go isn’t fully OOP) and how you can’t really create an image struct (nor even map) and have it default as empty. I’m sure the logic behind that is for performance, but why not expose the option of an uninitialised struct pointer behind ‘unsafe’ for those who are willing to accept that risk?

    • Freedom2 9 months ago

      > This applies to all languages with nil.

      This isn't true at all. C#, TypeScript and Kotlin are examples of languages where you have to explicitly opt into null.

    • leosanchez 9 months ago

      C# at least came up with Nullable reference types.

    • fithisux 9 months ago

      Scala has null for interoperability with JVM but Option is the way to go.

      Python these days has Optional[str] = str | None.

      Both are in the alternative.

      Dart hides optional as ?

      • hnlmorg 9 months ago

        Nil pointers aren’t the same thing as optional types in python.

        You can achieve the same thing as optional types in Go using ‘…any’ and the standard library has made use of that pattern since the very first release of Go.

        Nil pointers in Go are a completely different problem and there isn’t really a direct comparison in dynamic languages like Python nor Typescript because they use references rather than pointers.

        • benmmurphy 9 months ago

          I don’t think there is a difference between go pointers and references in python. For normal pointers to structs at runtime I suspect the implementation is very similar. Maybe it’s different for typescript because the compiler can protect you from null references statically and I assume if you are using python types the python type checker can also protect you at compile time.

          • hnlmorg 9 months ago

            A nil pointer isn’t a type. It’s a pointer to a type and that type hasn’t been created. In short, it’s an initialisation problem rather than a type system problem.

            Where “references” differ is you have to initialise the type to get a reference. In Python and Typescript, you don’t create naked pointers. You create structs then pass that reference in functions.

            The problem with Go is that you can create those naked pointers and then you need to remember to create your struct and then point that pointer to the struct. And you’re sometimes you’re incentivised to write code this way because Gos garbage collector needs to do more work than if you create pointers adhoc after (like references). But you can use pointers like references if you wanted too.

            So there isn’t really a direct comparison with languages like Python and Typescript because they don’t have the same primitive to begin with.

            There are benefits to Gos approach: you get more control over memory usage as well as improved performance. But like any idiom, it comes with disadvantages too. And the reason Go panics is because the alternative is the risk of silent memory corruption, which is what a lot of other languages can suffer from with pointers. Though this isnt to say that there are also languages that do solve this problem in a much better way too. But they’re are also a completely different language to Python and Typescript again too.

            So the comparisons to Python and Typescript simply don’t work for this type of problem.

            • go-nil-why 9 months ago

              There is no practical difference between

                  function useFoo(f: Foo) { f.Use() }
                  var foo: Foo
                  useFoo(foo)
              
              
              and

                  func useFoo(f *Foo) { f.Use() }
                  var foo *Foo
                  useFoo(foo)
              
              in this context. References vs pointers are equivalent here.
              • hnlmorg 9 months ago

                Actually there is. In your first example you’re passing an object as a reference, and in your second example you’re creating an empty pointer with no object attached.

                A better example will be:

                   foo := Foo{}
                   useFoo(&foo)
                
                That go code would be functionally equivalent to your Typescript code and if everyone used pointers like references in Go, like the above code, then you wouldn’t have any nil pointer bugs.

                Nil pointer bugs happen when you need to use pointers as pointers. (Shock horror!) And sometimes you do need a pointer with no object attached when solving specific problems are aren’t well suited for Typescript.

                Python and Typescript doesn’t have pointers. Those languages made different trade offs so they lose performance and memory management controls as a result.

                It’s no secret that Python needs to call FFIs to (for example) C++ code when performance and memory management concerns arise. Likewise with node too. In fact I’m in the process of debugging a 3rd party Rust library for Node that’s not handling pointers correctly and thus causing sporadic crashes of the node runtime. And that Rust library only exists in the first place because JavaScript doesn’t have primitives to write the required code natively in node at the performance required for scale.

                Maybe WASM will be the saviour here. But it feels like we are now just piling on more abstractions between the code and CPU rather than trying to learn how to use our existing set of tools better.

                • go-nil-why 9 months ago

                  No… I’m sorry I don’t have time to explain TS now. But that’s not what’s happening at all. What you’re proposing would be the equivalent of ‘var foo = new Foo()’. The default value in TS is undefined, which behaves generally isomorphically to nil.

                  • hnlmorg 9 months ago

                    I really really disliked working with undefined in Typescript. You might dislike the way Go throws an exception with nil pointers, but undefined types go too far the other way and can be a cause for subtle hard-to-find bugs if you’re not doing exactly the same kind of null checks.

                    That said, Im sure there are better ways of handling undefined types in Typescript than I was doing. I’m certainly not an expect in Javascript nor Typescript. Though a large part of that reason is because there are far far far too many design choices in those languages that really rub me the wrong way, so I usually limit my Javascript/Typescript usage the bare minimum I can get away with.

                    That all said, I will say I found Typescript to be a massive improvement over vanilla Javascript. It’s a pity we can’t just do away with JS completely and have TS run everywhere instead.

                • benmmurphy 9 months ago

                  python does have pointers under the hood. objects are passed via pointers. i think the implementation is tagged values in order to support dynamic typing and passing non-pointer values efficiently. but if there was no object tagging the runtime would be very similar to go. both have objects passed around as pointers using some form of garbage collection to handle memory safety.

                  the optional static type checking in python gives you a stronger typing than what golang does because you don't have to type everything as (None | Foo) and presumably its a type error to perform Foo operations on a (None | Foo) type.

                  • hnlmorg 9 months ago

                    > python does have pointers under the hood. objects are passed via pointers.

                    Of courses there’s pointers under the hood. It’s a runtime built in C++.

                    But that doesn’t mean that Python -the language not the runtime- has pointers itself.

                    > the optional static type checking in python gives you a stronger typing than what golang does

                    “Stronger” isn’t the term you’d want to use there. Python is not more strongly typed than Go just by virtue of it being more a dynamic language.

                    I do agree that the Go type system is pretty inflexible though.

notimetorelax 9 months ago

Did I read it right, the article used Claude to get the performance stats?

  • notpushkin 9 months ago

    Not a single source mentioned either so I think it’s made up?

    This is posted by the author, apparently, so I hope he can clear this up.

    • naveed125OP 9 months ago

      This is good feedback, I do have some in there but I will try to add some more links.

  • j0057 9 months ago

    The rest of the article was probably generated by Claude too, given the wishy-washy both-sideist utterly uncontroversial non-conclusion.

silisili 9 months ago

There's no real battle, except for language zealots.

Go gets a ton of things right for easy programming, and presents a super readable language with concurrency as a forethought.

Rust take a more conservative approach with a focus on correctness, and is the more performant language typically, for when you need it.

I don't at all see them filling the same niche, so I can't understand why this argument comes up so often.

  • hnlmorg 9 months ago

    I completely agree.

    The comparisons in this discussion with Python and Typescript are equally silly too because they’re not even strictly typed languages.

    I guess some people just want to feel like their preferences are superior and discussions like these allow them to cherry pick favourable metrics which validate their opinions.

colesantiago 9 months ago

There doesn't seem to be anything new here with Rust and Go that we already didn't know.

Reading the article, it looks like this article is generated by an LLM, as most things are nowadays.

wood_spirit 9 months ago

So can someone sum up what is said below the paywall fold?

Keyboard Shortcuts

j
Next item
k
Previous item
o / Enter
Open selected item
?
Show this help
Esc
Close modal / clear selection