from Hacker News

Rust vs Go: A Hands-On Comparison

by mre on 9/27/23, 3:22 PM with 94 comments

  • by leetharris on 9/27/23, 4:17 PM

    Just some random guy's anecdote.

    I learned both of these simultaneously in two quirky ways.

    I learned Go using the "Learn Go with Tests" book https://quii.gitbook.io/learn-go-with-tests/

    I learned Rust through a book called "From JavaScript to Rust" https://github.com/vinodotdev/node-to-rust/releases/download...

    The thing that stood out the most to me while learning these was their focus on composition over inheritance. Even though I really like C-based languages, I have never liked the inheritance push from the 90s-2000s.

    I really like some of the things in Go, but overall I felt I just didn't need it. .NET, Java, etc are more mature with comparable or better capabilities across the board. It's just hard for me to justify Go outside of stylistic preference.

    I really, really like Rust but ever since learning it almost 2 years ago, I still have not found a single use case for it. It's partially the maturity of the competition, but it's also that even in HPC, ML/DL, etc I've done just fine with the "legacy" stack.

    I would love to hear from people what their thoughts are on this. What are some scenarios where you see these languages thriving? What makes you want to use them? What's the best use case? Would love to evolve my thinking.

  • by barelysapient on 9/27/23, 4:29 PM

    I liked the article but I have a nit with the Deployment section:

    First, Go has other deployment options other than Docker or providers that support docker containers. For example, Google App Engine (GAE) has a command line interface very similar to Shuttle that enables deployment and hosting of your Go application. This is probably the best 'apples' to 'apples' comparison.

    Go and Rust are very similar in this way because both can be compiled to a statically linked binary with zero runtime dependencies[1]. This is important because both Javascript and Python are interpreted, frequently with run-time dependencies in addition to the interpreter, that can make hosting and deployment more difficult.

    Since both languages compile to a statically linked binary, they can be hosted virtually anywhere. Any old linux box or VCS with a modern os should work just fine. Optionally, one can serve from behind Nginx or Caddy for extra functionality.

    I've chosen this approach on a side project. It deploys with a simple `make deploy` command that runs tests, creates the static binary, and deploys the build asset over SSH and cycles the systemd service. It can be rolled back with `make rollback` which reverts by simply changing the symlink and restarting the service.

    [1] Assumes a modern OS.

  • by steve_adams_86 on 9/27/23, 4:31 PM

    I’m in a weird spot where I like both equally, but for slightly different reasons.

    When I have a dumb idea (they’re all dumb), depending on what its core features are, I’ll prototype it in Go, TypeScript, or Python. Inevitably I’ll want to or I will actually rebuild it with rust once it’s relatively stable.

    It’s like Go, TypeScript, and Python are pencil and rust is ink.

    Go is my favourite for strictly back end prototyping, but I don’t love it for processing data. That’s where Python shines. Then sometimes I’ll use a full stack framework like Next or Remix and build a backend there, but transfer it (or important parts of it) to rust eventually.

    My favourite deployment is a totally static site with a rust backend. But it’s equally easy with Go, and the initial development with Go is always way faster for me. It’s just the long term maintenance story that I don’t love with Go.

    Also, I prefer Rust’s type system quite a bit. It could just be intuitive to me. I do find several teams I’ve worked with on Go projects have not actually understood the Go type system, and they’ve run into pain as a result very regularly.

  • by tuetuopay on 9/27/23, 4:44 PM

    While the Rust in this article could be a bit better (though that's nitpicking and explicitly not the point), I find it showcases the biggest difference between the two: once you've laid out the foundations which takes a bit of time, the resulting code is a lot leaner. The handlers are short, concise, and the separation of concerns is natural. Good job the shuttle team, I now want to try your service :D

    The thing I feel like misses from the article is the more "second thought but vital" stuff, like observability. The tracing ecosystem for Rust is a blessing, and so boilerplate-free that you sprinkle it all over and never forget about it. From experience, having to manually access the span, put data in it, and defer close, that people don't bother doing them. Yay macros!

    With Go it's really easy to throw stuff at the wall, and it'll stick. Rust encourages you to put glue on it before throwing stuff.

    Also once you get used to axum and the various extractors, be warned, you'll hardly go back. It's so natural and powerful.

  • by hu3 on 9/27/23, 4:16 PM

    I found the article quite unbiased, specially coming from a Rust focused platform service.

    So congrats! Relevant part FTA:

    Which language is right for you?

    Go:

    - easy to learn, fast, good for web services

    - batteries included. We did a lot with just the standard library.

    - Our only dependency was Gin, which is a very popular web framework.

    Rust:

    - fast, safe, evolving ecosystem for web services

    - no batteries included. We had to add a lot of dependencies to get the same functionality as in Go and write our own small middleware.

    - the final handler code was free from distracting error handling, because we used our own error type and the ? operator. This makes for very readable code, at the cost of having to write additional adapter logic.

  • by danesparza on 9/27/23, 5:11 PM

    "Being originally created to simplify building web services, Go has a number ..."

    Um. No. Go was originally created at Google in 2007 by Robert Griesemer, Rob Pike, and Ken Thompson.

    Some key reasons for its creation include:

    Improving productivity of Google engineers - The creators felt existing languages like C++ took too long to compile and had other productivity issues. Go was designed to be simpler and faster to compile.

    Supporting concurrency and multi-core machines - Go has built-in concurrency features like goroutines and channels to allow easy parallelism and concurrency on multi-core machines. This was becoming increasingly important with multi-core CPUs.

    Improved code readability and maintainability - Go was designed with a clean, minimal syntax that avoided complex features like inheritance in favor of simplicity. This improved code readability and made programs easier to maintain. Better performance than interpreted languages - While Go is a compiled language like C++, it was designed to provide performance closer to those languages while maintaining some productivity benefits of interpreted languages like Python.

    Supporting networked systems and servers - Go has good built-in support for networking and servers, making it well-suited for building networked systems, web servers, and other server tools.

    References: https://go.dev/talks/2012/splash.article

  • by ilrwbwrkhv on 9/27/23, 4:34 PM

    Ya thanks for showcasing both.

    As is evident from the code, Go is simpler and cleaner. In 3 months it would be much easier to reason about than the Rust version.

    I am happy with our decision to migrate to Go from Common Lisp. I also wonder what is the long term memory usage of the Rust system. With Go I have found very little memory use which remains constant for months before machine swap.

  • by dingnuts on 9/27/23, 4:29 PM

    Why are there so many comparisons between Rust and Go? They don't seem to be particularly similar languages, either in use-case or style, but it often feels like the community wants there to be a competition between the two, which doesn't seem appropriate to me.

    Go should be compared to other high-level garbage collected languages: Java, Python, C# -- that kind of thing.

    Rust should be compared to C++ and C, but I'd also love to see it compared to other languages with advanced type systems, or contrasted with languages with more traditional or simple type systems.

  • by qaq on 9/27/23, 5:12 PM

    Rust async story is still playing out. Go green threads approach is pretty solid. My go to stack for personal projects: Go SQLC for db access https://github.com/a-h/templ (kinda JSX for go) for templating HTMX Postgres I have a feeling Mojo will ship 1.0 before Rust finishes sorting out async
  • by ianpurton on 9/27/23, 4:14 PM

    Rust supports more software engineering best practices than Go.

    Some people say the learning curve for Rust is steep. But, in a way you're learning how to write safer code using the latest in language techniques.

  • by kunley on 9/27/23, 5:54 PM

    Not to ignite a flame war, but to make a point about daily bread activities:

    what was the compilation time of both projects, esp. after using crates like 'anyhow'? How long did it take to have a full fix-build-test cycle in both languages? How many refactorings per time unit could a smart coder do in both ecosystems?

  • by blindseer on 9/27/23, 5:27 PM

    That seems like a fair article.

    The error handling in Go is SO verbose. When reading my code (or even reviewing other people's code) in order to understand at a high level what is going on, I feel like I'm squinting through a mesh wire window.

    Compare this example in Go:

        city := c.Query("city")
        latlong, err := getLatLong(city)
        if err != nil {
         c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
         return
        }
    
        weather, err := getWeather(*latlong)
        if err != nil {
         c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
         return
        }
    
        weatherDisplay, err := extractWeatherData(city, weather)
        if err != nil {
         c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
         return
        }
        c.HTML(http.StatusOK, "weather.html", weatherDisplay)
    
    To this code in Rust:

        let lat_long = fetch_lat_long(&params.city).await?;
        let weather = fetch_weather(lat_long).await?;
        let display = WeatherDisplay::new(params.city, weather);
    
    Maybe on first glance the Rust code can seem alien (what is a `?` doing there, what is actually going on with `.await`, etc) but when you are writing a 100k line application in Rust, you learn the patterns and want to be able to see the domain application logic clearly. And yet, there's no hidden errors or exceptions. When this code fails, you will be able to clearly identify what happened and which line the error occurred on.

    Prototyping even small applications in Go is verbose. And worse still, error prone. It's easy to be lazy and not check for errors and oops 3 months in your code fails catastrophically.

    I know a lot of people like Go on here but in my mind Go only makes sense as a replacement for Python (static compilation, better error handling than Python, faster etc). If you don't know exactly what you want to build, maybe it is faster to prototype it in Go? I still would reach for Rust in those instances but that's just me. For large applications there's no question in my mind that Rust is a better choice.

    Edit:

    people have pointed out that I'm not comparing the same thing, which is true, I apologize for the confusing. But even code where Go propagates the errors, it is much more verbose (and my point still stands)

        err := db.Get(&latLong, "SELECT lat, long FROM cities WHERE name = $1", name)
        if err == nil {
         return latLong, nil
        }
    
        latLong, err = fetchLatLong(name)
        if err != nil {
         return nil, err
        }
    
        err = insertCity(db, name, *latLong)
        if err != nil {
         return nil, err
        }
    
    And this is extremely common. More discussion in the thread below.
  • by Animats on 9/27/23, 5:25 PM

    Both are amusingly over-complicated.

    You don't need a database, just an in-memory cache. All you're storing is recent weather info, which you get from an external server. Caching is just to prevent hitting the external server too hard.

    Then the program becomes a single-file executable, which means you can get rid of Docker.

    If you set this up to run under FCGI, it can run on some $5/month shared hosting account.

  • by tgz on 9/27/23, 4:13 PM

    How long did it take to develop each web service? Which rps produces?
  • by intalentive on 9/29/23, 4:19 AM

    The Go syntax is so much comfier. Never written in Go but it looks attractive, might have to give it a spin.