Zero to Production in Rust
zero2prod.com
zero2prod.com
I'd be curious to hear from people who have been using Rust for their web backends, though. Beyond the classic selling points of speed and safety, what benefits have you found that apply to web development? Have you had any issues with the ecosystem or finding developers?
Rust shares many of the benefits of Go:
- statically linked binaries for ease of deployment
- good concurrency
- builtin testing framework
But it's a much sharper tool than Go:
- Really great error handling with Result/Option and the ? marker
- Like, really really good error handling, esp compared to Go
- Very pleasant logging and tracing
- Serde is fantastic at automatically serializing/deserializing datastructures
- Generics make Map/Reduce/Filter really easy and readable
- Really good project management through modules/Cargo.toml
- Crates.io is like NPM - really discoverable and easy to upload (with its goods and bads)
- FFI with Swift, C, C++, Node, etc to share code everywhere
- Builtin documentation
- Builtin benchmarking
- Powerful hygienic and non-hygienic macros
There's a lot more. I would say people choose Rust over other web frameworks not because of the language, but the emphasis on great tooling. Being able to test, document, benchmark, deploy, package, with a language that feels like a child between Go, JavaScript, and C++ is very attractive. Plus, it's fast - like really fast - and it's really obvious where optimizations can be made when the time comes.
I've worked a decent amount with Go, but I'm also frustrated with the size of projects balloon. When I compare my Rust and Go code, the Rust code ends up being much smaller (LoC) and much more dense/terse.
I've been working with it for a few years now, and I'm 100% willing to trade some complexity when doing Advanced Stuff (tm) for the 1st-class tooling. I hate having to decide which testing/documentation/building/benchmark/etc framework to use in other languages. I just want to build, not futz around with Jest/Babel/Webpack/Poetry/PyEnv/Opam/CMAKE etc.
If you're looking into using Rust for web, I recommend https://github.com/http-rs/tide - it's the most pleasant web framework I've ever used.
Yes, but is it more readable too? The obfuscated C contest also tends to produce a lot of dense/terse code, but you probably wouldn't want to use that in a production system...
The density comes from being able to share common functionality behind generic interfaces, so I don’t have to reinvent the wheel for every new project.
I find that most projects I make look the same internally, unless I start pulling some “clever” optimizations like custom allocators or self-referential structs.
Of course, you can write hard to read code. But that's not typically what comes out of a process of writing production software.
That's not to say that someone with no experience will fully understand the ownership transfer & borrowing that's happening, but that's just stuff you need to do for the compiler. Reading code that already compiles, you can mostly ignore that and focus on the program logic.
Especially for someone unfamiliar with rust, it can be hard to really know why someone is using self, &self, or &mut self. Yet the code reads the same.
That's the funny thing about rust.
v1, ok1 := <-c1
Not sure what is difference between := and = and what is that arrow. But if I'm going to do something with the language - I guess it will be easy to find out. Like with Rust.
The major exception to this are cases where authors use macros to invent their own DSL inside Rust code. This sort of thing seems common in the Servo codebase, amomg others. It reminds me of C macros or Python metaprogramming magic, both of which directly led to some incredibly difficult past debugging experiences.
You don’t need to know much more than Python/Ruby to get something done in Go, whereas Rust needs a C++ or Scala or Haskell or whatever background.
Rust places too much mental workload on a programmer to make the compiler happy.
The Go compiler is much more human friendly.
Swift is a nice mix of all worlds, IMHO. Would be nice to see that get more server-side use.
At our Scala-centric company, Go gets used a lot more for one off tasks and microservices.
We rebuilt one Go service in Rust just to get familiar with it and no one has written more Rust since.
“Needs” is a bit strong but “may find some things familiar and therefore easier” is a common refrain.
fn foo() -> &str {
&String::from("foo")
}
Does not work. Whereas coming from C or C++, you will appreciate the safety guarantees the borrow checker is giving you.Learning Rust coming from an OO background is also hard, as you have to adapt to the more functional approach of the Rust type system.
As for your example, even C and C++ devs would squint a bit at why that code doesn't compile: you're allocating a string on the heap, so why can't you return a pointer to it? Also, I think str vs. String is usually confusing at first, regardless of your background.
Yes, of course. Rust has some new concepts that will be confusing at first, regardless of your background. You still have to understand Rust's destructors to understand that example. I didn't mean to say that it is impossible for Rubyists to learn Rust, or that C++ devs would understand Rust immediately.
Perhaps a better example:
fn foo() -> &usize {
let x = 50;
&x
}I would say that the relationship between str and String was what most surprised me. I at first assumed the underlying primitive would be String since str is what you get when you borrow from a String, but it isn't. str is a built-in, whereas String is just a type defined in the (optional, this is a systems language) standard library.
If your environment can't afford memory allocation, you can't have Strings but you can use str just fine, that's a built-in. Since you lack allocators obviously you won't be going around minting new strings but str is perfectly fine for stuff like substring operations or comparisons.
Well here I am to deny it. I personally find Go super hard to write anything that is not a few lines of glue code (and in that case I just reach for Node).
Rust made me lazy. It's just so easy to be guided by the compiler and the architecture that emerges naturally is just so beautiful and easy to navigate.
I don't like Rust for its safety guarantees, I just find it pleasurable to write and even better to read. Safety is just a plus.
I'm not saying this is a universal truth though, Rust just fits my mental model. It isn't even a matter of past experience: I code frontend JS for a living (though I've been exposed to all kinds of code throughout the years) and I actually enjoy JS.
I tried really hard to like Go. I just couldn't.
> I tried really hard to like Go. I just couldn't.
I would expect something opposite for most of programmers. Especially, because Rust support in VSCode/JetBrains IDEs was not very good for many years. However, during talks with various people, I have noticed the Go was natural choice for many former Python or Ruby devs, while Node/Java guys had problem with adapting to it.
Personally, when I take a look at the Rust code it feels like some ECMA Script in a compiled world.
Swift's Codable is doing code gen under the hood, so it also has a binary size and compile time cost.
Swift also doesn't scale that well with core size like many other languages do, so your compile time is not easily fixable with large thread ripper style workstations.
Do you have similar issues with golang and rust? At least golang was designed to be compiled fast.
Compile times are definitely a pain point in Rust, but from what I've heard the Rust compiler is pretty good at scaling to multiple cores (my machine only has 2, so personally that doesn't help much).
I think it would be good if one dabbles a bit with both and pick one which one feels resonance with. That being said probably one need to give a bit longer time for Rust to be comfortable with it.
Ouch, OOP in Go is just asking for a trouble. "Inheritance" by embedding anonymous struct/interface should be rarely used. Playing with a magic dependency injection, reflection mocking often do not leads to anything good too.
> In addition in Go I find the project setup and settings like GOPATH, GOBIN quite confusing.
I guess you work on Linux :) Yeah, it is weird, I am glad they have improved that flow by guessing empty GOPATH will be equal to $USER/go/ directory. GOBIN is actually not needed, only if you have used dep dependency manager, which for many years is deprecated.
Async rust is very messy and a pretty big issue atm. The tooling on the IDE side is subpar compared to Go.
It's not like JavaScript or Go where everything is just built into the language afaik.
Personally, I’m looking forward to the next few years when more and more companies reveal what patterns they use with Rust development. I think Rust is missing the kind of boost that Rails gave Ruby, for example, or that TypeScript and React gave Node/SSR. Or that Pandas gave Python.
1. https://doc.rust-lang.org/cargo/reference/specifying-depende...
2. https://doc.rust-lang.org/cargo/reference/features.html#depe...
To use? Eh - I just turn on async_std with the Tokio flag and write go-like code with async tasks. No issues thus far.
There are some pain points with cancellation in Rust. Dropping a future cancels it, but that requires ownership over the future, and may cause unexpected behavior due to the nature of futures state machines. This, along with many other issues, is being discussed by the async working group: https://github.com/rust-lang/wg-async-foundations/issues/65. If you have specific suggestions, or a problem that you faced, please open an issue! It is really appreciated - async rust's problems cannot be solved without community feedback and engagement.
I won't try to play as a Go devil advocate, because I don't feel good enough today to start such discussion.
What is the worst thing I have experienced in Rust? Lack of good packages, especially if we speak about cloud stuff (rusoto and google-api-rs). Human resources put from cloud providers are very scarce. As longer I dived into the Rust the more I felt I am alone freak, because I had to work more hours to achieve the same result as in Go, PHP, Kotlin, TypeScript, C#. My use case of Rust was a GraphQL gateway (gRPC+HTTP) and after few weeks of idea verification the project was dropped. Mostly because instead of developing business logic the team was focused to implement own libraries for Google Cloud and other SaaS offerings on their own. Ridiculous, but Mozilla devs [0] went the same way by taking the whole job on their shoulders and they were generating subsets of APIs, because they cannot find any good, official libraries. It is frustrating and time consuming making Rust completely no-go for many teams.
> I've worked a decent amount with Go, but I'm also frustrated with the size of projects balloon. When I compare my Rust and Go code, the Rust code ends up being much smaller (LoC) and much more dense/terse.
I can exactly the same about the Rust. I think it was not the problem with tool but with something between chair and keyboard. I had a pleasure to see and refactor very bad Go code and actual ballooning was never a case if you have applied Clean / Solid architecture. The "ballooning" was actual something nice, because anyone in the team could pick the repository and take care of implementing new business logic.
The number of lines of code is quite bad metric for Golang, because famous "if err != nil" takes a nasty 3 lines, while in many languages you can write it using single liner. In my humble experience, the number of lines of most Go web apps can be often compared to TypeScript apps. Also many web apps that I have developed usually had hefty 2-5k LoC and it is not bad result if we consider C# or Java web apps.
> - Powerful hygienic and non-hygienic macros
Yeah, having marcros in Rust is a gem.
> - Builtin benchmarking
Same as in Go. See testing.B.
It is weird you have pointed out
> - Builtin documentation
Same as in Go. See godoc.
> - FFI with Swift, C, C++, Node, etc to share code everywhere.
I rarely see the case for FFI in web app - I don't imagine even using it like that. It was common case for chart, TeX document or other web to image rendering to embed some library. I think it is a bad approach and the best currently is just run that code as separate binary, because it helps in maintainability of a third party dependency and your devops teams will love that decision.
As a person who rewrote the rxi/lite [1] to Rust I can only say I am terrified how many times I had to sacrifice having unsafe calls to just use some C code in my Rust application.
> - Crates.io is like NPM - really discoverable and easy to upload (with its goods and bads)
Personally, I feel Go/Deno/Docker flow of the installing dependencies is more convenient. I don't project names that do not sound what they provide. Naming thing is bad and I often see many developers are scratching head to find some unique name, so it could be easier findable in NPM or other repository. They are wasting time at looking for something short and funny and not something that is easily discoverable.
For many years, my only way of looking for dependency is GitHub. It helps to verify and investigate the quality of the code before adopting it in the application. It sometimes also results in contributing back to the project, because I already know where the project is located.
> - Really good project management through modules/Cargo.toml
It is really complex I'd say. But Cargo.rs would be nice to have :P
> - Generics make Map/Reduce/Filter really easy and readable
Matter of time in Go. :)
> - Serde is fantastic at automatically serializing/deserializing datastructures
Oh, that's true. Go has numerous serializers (for only JSON there are about 20+ nice looking implementations) and it is still interesting to see new ones, especially because they bring many non-obvious optimizations. Kotlin serializer is also top notch thingy. I recommend to use protobufs types in Go - even for domain models. It is interesting how with Go2 can improve the situation.
> - Very pleasant logging and tracing
The log package is nice, together with macro log! is really pleasant to look at it how concise it is to fill some context parameters around the message. I think it is like Logrus in Go, except the syntax it has the same features. There is also Zap package that sacrifices readability for performance. There is no problem to embed number of line of code, calling function in Go logs either.
Tracing? Yep, it feels the same for both languages I think. I had used OpenTracing (Jaeger) and I hardly can say I miss something from one or other language. Maybe some macro/annotation wrapper that will be added on compile time? However, I rarely see any use case right now for complicating such thing like tracing.
> - Like, really really good error handling, esp compared to Go
Hey, it is not that bad! :D But well, as long as you use Goland you don't care about the if-err-non-nil.
If you switch for few days to Java-world you will be like in hell after developing in Go, because the error-over-exception flow is really helpful to provide code that does not have any kind of error and you, as a developer, can guess what happens in specific line without any println("1") or debugger. Monads are nice but I will not trade current if-err for them.
> - builtin testing framework
AFAIK all modern programming languages includes some testing framework: Kotlin (kotlin.test), Zig (std.test), Elixir (ExUnit) etc. I am glad both Rust and Go joined that club.
> If you're looking into using Rust for web, I recommend https://github.com/http-rs/tide - it's the most pleasant web framework I've ever used.
It is interesting, but I think even today there is nothing that I could label as a "framework" in Go world. You wisely choose the best packages matching your needs. The situation has improved with many libraries providing a GraphQL endpoint.
> I've worked a decent amount with Go
Reading your post, made me think you don't have good Go experience and it sounded bad IMHO. I guess you had nobody proficient enough in production-ready Go apps to evangelize it within the team or there was no reason to use Go (yep, it is not a perfect tool). ;)
One thing I hear from people more broadly is that Rust’s lower resource usage, in today’s cloud based environments, translates directly to bottom-line savings. It’s not so much speed directly as it is less CPU and memory utilization, letting you do more with less. That matters when you’re paying for what you use.
For us, the safety is the critical reason to choose Rust - particularly the thread-safety. Also the relatively small memory footprint compared to something like Java. Performance hasn't driven our decision at all - the number of requests per second is very low. It's correctness that matters.
We are a bit unusual because customers have locally deployed servers rather than servers managed by us, so bugs are harder to fix.
We haven't hired any developers specifically for their Rust experience, but have instead trained internally and now have a group of about 10 Rust developers working across a couple of projects. I would say that training like this has been relatively straightforward. One of our hires, a recent graduate, had some existing Rust experience with personal projects, and this was particularly beneficial in helping the team to bootstrap.
The ecosystem is surprisingly mature. Async is probably still shaking out somewhat, but that's moved on hugely in the last couple of years, and we haven't had any particular problems.
Overall the experience has been hugely positive.
The biggest barrier to adoption, in my opinion, is that people are scared of learning Rust because it has a reputation as being difficult. I don't think it's any harder to master than some other major languages such as C++ or Java - although perhaps you have to understand more at the beginning: ownership and borrowing are right in your face.
We evaluated several frameworks and liked the model that Actix has and also its relative maturity (for example documentation / examples). It has been pretty straightforward to work with - including doing things like custom authentication and REST endpoints that dynamically exist.
For the drama, I guess you are talking about the situation that used to exist where Actix had hundreds of unsafe blocks. That's pretty much resolved now, with perhaps a handful of performance critical unsafe blocks left.
Then they really should think about changing their name, seeing as actix the broader low-level framework is supposed to be all about Actors.
I'm not passing any judgements, I just know that people were somewhat worried about the future of the project after its creator very publicly washed their hands of it. Sounds like it's still going, which is good to hear
Does Go offer similar guarantees for concurrency?
For races related to external resources, or caused by misuse of OS IPC mechanisms (which is the future thanks Spectre), it offers as much protection as any other language.
What was the process of proposing rust and getting buy-in like?
We've actually bootstrapped relatively easily. We have a some developers who have picked it up quite easily, perhaps with some pairing. We run an hour session every week where all the Rust developers get together to share production code and present on Rust topics. The discussion focusing on Rust production code has been particularly helpful.
I would say the biggest barrier is developers not wanting to jump on yet another "this language will solve all your problems, trust me" train.
My experience in my 15 years in software, is that good developers solve problems, not languages or tools.
Never underestimate what a team of overly enthusiastic junior engineers are capable of :-)
Meanwhile the junior-intermediate devs spend most of their working hours writing code, so they are extremely sensitive to the 5-10% productivity bump that could be gotten by using {nicer language, better build system, static analysis tooling/sanitizers, ${TOOLING_IMPROVEMENT}.
I'm talking real advances in productivity. Imagine working on a C++98 codebase today in 2021 because you're working on a very large industrial product that's been around for decades. It can be demoralizing, like having to write COBOL or FORTRAN.
Package managers, which ones?
Perl was the very first, almost every language has their own, and even the growing C++ ones are able to use binary libraries on their package managers.
Python's Poetry comes to mind.
In terms of influences, here's an OCaml attempt to replicate cargo (I wish them well): https://github.com/OCamlPro/drom
Klabnik also mentioned Python's poetry below. Yarn as well, since Yehuda Katz was involved.
Note: I seem to recall you're an Ada dev, so I should mention that as far as build tools go, I've always really appreciated gprbuild, which is far superior to other build tools of its vintage. If it had a built-in package manager like cargo, it could compete with cargo.
I like Ada, but no not an Ada dev, rather JVM/.NET/C++, I rather go polyglot.
And in this regard, while cargo is nice, I don't see how it is better than Maven (I don't suffer XML allergy), NuGET, vcpkg/conan, specially because it only does source packages with npm like levels of dependencies.
Anyway, yeah, Maven is very good and competes with cargo, as well. I agree.
Nuget? Absolutely not, based on my previously undefined rubric. Unlike Maven and cargo, it's just a package manager unless it's recently been expanded (I'm not much of a Windows guy, so not sure). With some pretty crappy defaults as of a year or two ago. No idea about vcpkg or conan.
(I probably should have specified that I'm comparing among package manager that include build tools - or vice versa).
Edit: I suspect cargo will get around to binary packages at some point, as soon as that limitation is resolved in rustc using something like TUF. cargo has not been around nearly as long as Maven.
And for the record, despite my love for functional programming, I also have a strong appreciation for Java (not as much as Ada, but it's a great language when used by competent devs).
... a programming language designer should be responsible for the mistakes that are made by the programmers using the language.
[...]
It's very easy to persuade the customers of your language that everything that goes wrong is their fault and not yours. I rejected that...
http://blog.mattcallanan.net/2010/09/tony-hoare-billion-doll...If anyone is unfamiliar with Tony Hoare he's the creator of quicksort, Hoare Logic, CSP, worked on Algol, created null, holds a turing award, and much more.
At 15 years I thought the same. At 25 years I have come to really appreciate some tools ability to help.
There are a lot of tools that claim to help but don't. Beware of tools that try to manage complexity - what you probably need is to reduce complexity. I'm looking at Eclipse and Matlab in particular, and anything similar (think enterprise software).
Git - awesome. Using it on 1-man projects too.
Sanitizers for C++ I've seen these identify bugs that hadn't manifested visibly yet.
Rust - well I want to write more because it doesnt require additional tooling to write correct code. Remember reducing complexity.
>> At 25 years I have come to really appreciate some tools ability to help.
I've come to find there is a bidirectional connection between good developers and their tools. Good devs recognize and gravitate towards systems that help them thrive, and away from painful ones. Good tools draw in good devs, and the community gets stronger as a result.
IMHO, One hallmark of less-than-stellar devs is a tendency to stick with tools they are familiar with, because "it works." Great devs could get by with a magnetized needle and a steady hand, but they know there is better out there.
>> thread-safety
In 2021 what web development stack is making you write threaded code?
>> correctness that matters
The compilers guarantee end at the edge of your processes address space. You still have the same big problems as everyone else:
1. Are you building the right thing?
2. Does everyone understand the domain adequately to do their job?
3. …
101. Do i have a race condition
Except you have a self-inflicted problem - you’re slower than a dev team working in a traditional language (java, go, python, …) and you can’t easily hire talent.Go? I've had thread related bugs in go related to serving our UI.
Generally any reasonably efficient stack where you need to pull on more than 1 CPUs worth of resources to quickly serve a request. And most stacks where you need to make multiple requests to other services simultaneously to quickly serve the original request.
The framework might not make you but that doesn’t mean your code won’t need such things.
Go uses "green" (user-level) threads.
If anything my experience is that it's quite productive to work in Rust. For example, compared to Python (which you mention) we catch bugs earlier (with the compiler) rather than later (system test) and so development is faster. Shift left.
It's particularly important for races because they are some of the most difficult bugs to find and fix.
I'm radically more productive with Rust vs Python (having been using both for >5 years). Our codebase is split between the two and I'm working to move virtually all Python code to Rust because it's become such a burden.
* IDE experience is considerably worse in Python due to lack of types (we use type hints aggressively, but that only helps so much and not at all for 3rd party libs).
* Working with JSON is considerably worse in Python in my experience.
* Package management and building is considerably worse in Python. It's still difficult to manage a many-package project and get what Rust provides by default, like workspaces, cryptographic lockfiles, etc.
* Error handling and other patterns in Rust make it far easier to write in a robust way or otherwise correct in the future. Rust is far more resilient to errors during refactoring - I'm very afraid to touch some of our Python code, by contrast.
* Mypy is nowhere near as good as rustc. Mypy errors are traditional "expected blah got blah", or sometimes "expected List[T] got List[<nothing>]" and other unhelpful errors. Rust errors are almost always expressive and helpful - often telling you exactly how to fix your code (to an insane extent - like using a heuristic to tell you where you probably forgot a curly brace).
Mypy is impossible to use with the strictest settings. Lack of recursive types makes working with JSON blobs impossible, makes it impossible to do circular generics (you can't do what Rust does with From/Into as far as I know because of this).
And then there's shit like circular imports being a runtime error, and other little papercuts in the language that are just littered everywhere.
I actually really like Python a lot, despite all of this, but I'll never choose it for web services again.
And hiring hasn't been a problem at all at my company, nor was it at Dropbox where I worked previously
We will also run a training course next month just based on the Rust book plus some workshop examples.
I started learning Rust and C++ at the same time, coming from a world of Java, Python, and Ruby.
If I need to solve a problem in C++, I would much rather write Rust because the compiler actively screams at you when you do bad things.
And in my opinion, the challenging part is learning what those bad things are. In other languages, the compiler doesn’t care. You immediately see your program crash or things fail in subtle, hard to reproduce ways later in production. Rust changes that model.
I disagree with you on the documentation. I think the Rust documentation is superb. I have yet to see any language give nearly as meaningful information when your code fails to compile. Secondly, Rust front loads and throws issues immediately in your face, as opposed to having you discover problems in your code at runtime. I don’t think that makes Rust difficult - it’s forcing you to think about your choices. And particularly it’s making you think about concepts that perhaps you completely overlooked in other languages - thinking about stack vs heap allocation, moving semantics, shared ownership, etc.
If you want to quickly prototype something, Rust isn’t the best language IMO. But if I have a choice between C++ and Rust, I’d go with Rust anyday. If I was starting a new project from scratch and it was more of a web service, I might go with another language over Rust only because I’m more of an expert in Java, Python, etc. and it’s easier to hire developers for those languages than Rust.
But I’m biased. Rust has a special place in my heart. It’s the first language I’ve learned in many years where I felt some sense of accomplishment and did not at all feel like “Oh great, another tool that fundamentally doesn’t add value.”
For my use cases involving Java and iOS interoperability in a multi threaded environment, nothing else came even close.
Oh there is just no way it's harder to learn than C++. C++ has the problem that it's all of C (almost everything from C is still in there) plus all the stuff bolted on to achieve C-with-classes when OOP was the new hotness in the 1980s plus then several further evolutions to add major new features, not always in a very consistent way.
There is a sub-tribe within C++ that wants a simpler "subset of the superset". They believe C++ could be made into a healthy modern language which is practical to teach as a first language to students and still has excellent performance and hardware compatibility. But if you speak to members of that tribe they invariably feel there's just a few little tweaks to make to the superset first... and I just don't think their dream will ever actually come true.
Rust does have a lot of complicated stuff, but almost all of it secretly turns up if you wanted to be good at C++ too.
For example, borrowing versus ownership isn't explicit in C++ but it's necessary to understand what's actually going on so that you'll find Stroustrup has actually written proposals for C++ that formalise this exact notion, they just can't be checked for existing C++ as they are in Rust.
We probably can't agree on which half. :-)
No, not literally.
This is mostly true. As FP and Rust have shown us, if you can treat an input data set as immutable, while building up the result data, you have a much easier time. I bring those patterns into my C++, which speaks to your point.
I'd offer one exception though: mutability xor aliasability on a per-object basis is something I wouldn't take to my C++ code. It may be the core of Rust, and it's a neat tradeoff that enables some of its other benefits, but the indirect complexity cost is too high, IME.
> Oh there is just no way it's harder to learn than C++.
I think the jury's still out on this one.
C++ has complexity, but has the benefit of a gradual learning curve. You can start with C, slowly add `virtual`, then add templates, then add the STL.
Rust has just as much complexity, and forces it all on you at once. Because of that, it is harder to learn.
Some people say Rust's rules are intuitive. I have to disagree. These years of working with Rust and teaching Rust to others have made it clear to me: though the basic concepts are simple, the architectural implications and workarounds are not.
I'm not saying C++ or Rust is better. But C++ is slightly easier to learn, IMO.
I commonly hear "But it's hard to learn how to use C++ safely!" and I generally agree with that. ASan has made things much easier in that regard, but C++ will probably never catch up enough for some use cases.
Rust has "interior" mutability for when you need mutable access to shared data. It works very similar to the "mutable" keyword in C++ except that it's always type-driven, as opposed to something being specified as part of a declaration.
But then, once your code has RefCell<> everywhere, you're incurring runtime overhead, at which point one might as well switch to a language that makes those tradeoffs easier to wield.
This'll get even better in the near future; a lot of languages are improving in this regard (Cone, Lobster, etc).
The runtime overhead is quite trivial (a single word per object IIRC, which is accessed w/ simple increments/decrements/checks as needed) and work is ongoing on abstractions that don't incur any hidden overhead (called 'GhostCell').
If it's a rather short-lived object and we don't need to reuse its location, it can be in a Vec, which involves bounds checking and O(logN) calls to malloc, which is not amortized. Sometimes we can put short-lived objects in an arena, which is the best option, but they are very memory hungry, which has its own costs.
If it's longer lived, we can use a Vec with indices or a type-pool, but we risk use-after-"release", which can be a privacy risk and defeats some of the nice "sanity" properties of Rust code (this is still a good option in some cases, IMO).
If it's longer lived, and we don't want those drawbacks, we have to fall back on something like Rc or generational indices, which both have their runtime costs.
IME, only a very specific kind of use case can avoid any of this runtime overhead. When you get to the more complex use cases, especially with lots of unavoidable state, these overheads appear more and more, and make the whole situation a little less clear-cut.
(This was a rather hand-wavy explanation, I can make it more accurate if you'd like)
Having seen teams go Java -> C++ and other teams go C -> C++ I don't really think that's the case. The former leans on shared_ptr everywhere, the latter ends up hitting footguns with implicit behavior around move semantics, RAII(seen more than a few use after free here) and the like.
ASAN and other tools help, but assume they are run regularly and kept to a level of rigor I haven't always seen in codebases.
In both cases, one benefits from input from those experienced in the language.
(not saying c/c++ is better than rust, just commenting on the difficulty)
With C++ unless you have someone experienced on a team there's a host of "unknown unknowns" that feel like the team is making progress but then having to pay the cost when it blows up and usually when schedule pressure is near the highest point. Same for the build systems, retrofitting LTO is not a fun experience.
Rust in contrast calls out many of those things upfront, I've found those guardrails prompt conversations in tradeoffs and conscious choices(to say use Rc/RefCell) you'd be making anyway in C++.
I'm not attacking rust, btw. We all know c/c++ isn't great. Theres plenty of aspects where rust is better, and plenty where c++ is better.
But we're just talking about difficulty, and in this context it's fair to say rust isn't necessarily better than c/c++.
Language difficulty I'm having trouble seeing how C++ isn't harder there. You have decades of features, lots of gotchas that I could rattle off and a bunch of implicit behavior that you only learn about when it blows up at runtime(if your lucky). Heck, I've seen heap corruptions stay latent in codebases for 6+ months only to have a new field added that causes them to cascade into a failure that took 3 weeks to debug across 2 different teams. I've still seen modern compilers not catch basic use after free scenarios in code today.
I won't disagree that you can get code to compile quicker in C++ but if your goal is feature velocity you're better served by a high level language. If your goal is stability, performance and memory which is the domain of systems programming then I think C++ has a bunch of hidden failure modes that Rust frontloads in an elegant way.
There are projects where you'd pick C++ for portability on platforms LLVM doesn't support or momentum with existing codebases but if I had to pick between the two my personal bias is towards Rust for all the reasons above.
Nobody here is saying c/c++ or rust is better after their respective learning curves. No need to be defensive, they're just languages, and every language has its strengths and weaknesses.
To the point: modern c++ is easier to learn nowadays. shared_ptr and unique_ptr are as easy as Rc and Box, but without the borrow checker making things harder. One doesn't have to deal with the decades of difficulties when learning c/c++. One can choose to dive into a terrible legacy c++ project just like they can dive into an unsafe mess like Actix, but thats hardly a reflection on how easy to learn a language, just the codebase you're learning from.
And rust's front-loading of problems is a great thing! It also makes it harder to learn, which is the point here.
I would argue that if you don't understand the ownership of your data you don't really understand C++(or C) and you're still learning the language. That's usually the big shift I see when developers who have a background in higher level languages moving down to C++. I've seen plenty use after free and heap corruption happen in C++0x11 and onward codebases both staying completely within modern best practices and when code diverges since there's no guardrails(ex: std::string::c_str, std::unique_ptr::get and the like).
Once you include all the footguns that exist(yes, even in modern C++) and the legacy parts of existing codebases or code that diverges from modern C++ that's why I think it has a much higher learning curve to be producing code that is at the same quality as a passing compile from Rust in aggregate.
If one is just designing a game, or a command line tool for internal use, one needn't make it completely rock solid, and C++ has the better learning curve.
If one includes the requirement to make it safer, Rust has the better learning curve.
And then if someone actually values memory safety, they use a GC'd language and laugh at how many engineer-months and proofs it will take us to make code as safe as theirs. Sigh, such is life...
This is an unsubstantiated claim. If Rust makes something hard, that doesn't automatically make it universally bad. Nearly every large Rust code base I've seen uses reference counting (both `Rc` and `Arc`) a lot. Moreover, it is not practical to write general-purpose code avoiding both GC and RC because the need for shared ownership will inevitably crop up at some point. So ruling it out as code smell is a bit dogmatic.
It is also worth noting that ownership semantics is one approach (among others like GC) to memory management. In future, more languages will have some sort of ownership semantics along with GC. Nim's ORC is currently a good example.
Yes. There is a "c++" which is simple, consistent, and fairly easy to use, but it's buried in the cruft of decades.
The biggest complaint about Rust's "hardness" is ownership and move/borrow mechanics. Modern C++ has std::move, ownership, and lifetime considerations.
Add to that smart pointers, initialization, a bloatastic std lib, an even more bloatastic pseudo-std lib that you need if you have to interop with anything older than 14 (boost), umpteen different build systems, compilers, toolchains, and all that on top of almost-not-quite-C-superset, and you have a massive surface area.
On another HN thread, someone commented that developers took 3x longer to get to speed in a Rust codebase than C++. I say no way, Jose. I was committing PRs in Rust within a few months. I still don't feel comfortable in C++ of any serious complexity. That's with 2 years of C experience, 1 yr of intensive C++, and like, a few months of rust.
Last week it was a combination of forgetting the exact specifics of RVO hitting the implicit copy constructor that was causing things to go sideways. The number of implicit behaviors are pretty wide and things like move semantics still feel pretty brittle.
I had some really mission critical camera driver stuff I was working on and just never felt secure that there was a massive bug lurking, despite code review, Valgrind and testing. But on that same project, I had enough confidence to write a small daemon in Rust, with like...maybe 60h total experience in the language. Huge peace of mind knowing the code does what I expect it to do.
My eyes can’t possibly rollback far enough.
I don't think rolling one's eyes is the appropriate reaction when someone pushes for it. Depending on your requirements, there are definitely cases when rust is the best choice for a web backend. Especially when moving fast and breaking things is not an option, such as in the author's case.
I see a big Rust benefit when you’re writing systems level code or you’re writing once and use the code as a library on Android and iOS.
But what type of logic are you writing in your services that requires Rust’s safety guarantees? Or is it that you’ve found you can respond to the same performance metrics (total requests per second) with fewer hosts because there’s no JVM or garbage collection overhead?
But this is backend web development, the major languages are more like ruby/php/python/js/java. You might write a C++ extension as an endgame move, but for webdev it's never an opening move.
I built using actix 1.x, serde, chrono, rusqlite. I originally tried diesel but it doesn't support certain relationships that are common in SQL. I made my own traits for common CRUD and ORM patterns but I'm not happy with the amount of boilerplate code needed to implement them. I could clean them up with macros but I'm still rethinking the approach.
I did implement my own (unpublished for now) crates to output jsonapi formatted messages and handle uuid primary keys in sqlite, serde from the DB and json.
Overall I'd say it's comparable to coding a web app with C++, but perhaps more straight forward. I added my own middleware for Google Auth without much trouble. It definitely seems to be a "if it complies it works" type experience.
Concurrency is pretty trivial as well with async/await and tokio.
Really good libraries like tracing, tonic, etc.
Plus the tooling is really strong. Package management and building is easy, static bins, etc.
I run a company that uses Rust and hiring isn't hard - it's an advantage. We get really great people who are looking for a place that uses Rust. Or we just train people up - it's not a very hard language.
A dynamic language has no clue what these types are so you find out what you did wrong by refreshing the app at runtime.
In Rust it tells you instantly, so it reduces the iteration time.
Many web apps fit this “map data from A to B” domain, so having a specification for types A and B and getting the compiler to check all variations seems like it would save a lot of dev time.
Out of the type systems available I like Rusts best as it seems to map well to JSON using its enums feature to represent null or polymorphic types.
But you need to pay the learning curve of Rust, which I am still not sure it is worth it.
"Zero To Production will focus on the challenges of writing Cloud-native applications in a team of four or five engineers with different levels of experience and proficiency."
It's useful for that. It's not about how to program in Rust. It's about how to set up a production web application in Rust. This is more of a tooling problem.
£35 Individual, £100 Team, £600 Company, seems like fair/straightforward pricing for a team that has members who know some Rust but haven't built out a production API with it yet. I wish every tech stack had a book like this, and I think it'll be particularly valuable for an exciting new language with a learning curve like Rust.
I will note, though, that I'm currently consulting on a team that's transitioning away from Rust for their startup's GraphQL backend because they found it too slow/difficult to onboard more junior engineers to the language. Personally I find the Rust code pretty great, but the learning curve (and perhaps even talent cliff) is something to keep in mind if you're considering Rust for a standard web api use-case.
TypeScript is probably a more appropriate choice for most situations – still a rich, powerful type system, great performance, and much faster compilation times (and thus iteration speeds) and much, much easier to find engineers who will be productive within a month on your codebase.
EDIT: To be clear, I still consider myself a fan of Rust, and am keen to have an excuse to use it.
Plus, if you're building a backend for a webapp, TS allows your engineers to build full-stack features in the same language, avoiding context-switching, code duplication, and other overhead.
Plus it’s a binary I can drop anywhere, will always be backwards compatible, no updated dependency is going to break it, I’m a fan of Go.
Other than Fiber, the techempower composite framework benchmark[0] ranks gin (go), nodejs, clevergo, and fastify (node) one after the other, all scoring right around 1600.
To be fair, Express is half that good at ~800, but upgrading from express to fastify should be fairly straightforward for those seeking better performance; the much newer fastify has a similar (often compatible) API.
For comparison, Python's fastapi is only 1200, and Django/Rails a mere 250, so lumping Node in with Python and Ruby doesn't make much sense; it'll often perform an order of magnitude better.
For computation-heavy workloads, multiprocessing with Node remains a challenge; Go's a more clear winner there.
[0] https://www.techempower.com/benchmarks/#section=data-r20&hw=...
Edit: oh I get it, "too slow" as in "compilation time". I guess they really needed excuses to move back to another language.
Though I will note that the Rust compilation times were also far more painful than I'd expected – even the tiny (~5k sloc) app at hand would take what felt like a minute to recompute the red squigglies in my editor.
TS felt slow if it took 5-15sec, and Sorbet (for Ruby) was usually <3sec IIRC, so a minute+ was pretty bad.
Rust was in the realm of "just don't use this language if you have ADHD" since you really can't make progress without the compiler's exacting approval, and I'd constantly get distracted while waiting.
You can use cargo check for that, which is fast. It's only for running tests or the like that an actual build is required. Rust also has IDE integration via rust-analyzer, so the error-marking "squiggles" can be recomputed incrementally.
As someone that might be similarly diagnosed for the same reasons, your comment does make me more optimistic of achieving something significant in my life, though.
At least one team member was a recent MIT course 6 graduate who was able to pick up other new technologies quite quickly without any oversight or assistance. They could get things done in Rust, too, but after a few months felt far from confident.
The performance has been excellent compared to similar offerings in django and rails while having better dev productivity than the equivalent nodejs libraries.
Developers who could get by professionally in Python, but were not particularly good, have struggled with Rust.
However, we have several developers who joined us directly from their undergraduate degrees who have picked up Rust extremely easily.
Compared to Rust's normal debug builds Typescript does not have any noteworthy compilation speed advantage in my experience. In fact I find incremental Rust builds very similar if not faster in some cases (of course there are extreme exceptions both ways; I've used a library that added 40 seconds to every Rust build, but also frameworks that didn't make a noticeable difference after the first build). Meanwhile the quality of TypeScript error messages is much worse. Also sometimes debugging gets confusing because TypeScript has a couple blind spots and in the end it still turns into JS with all its quirks. I find Rust's type checker significantly more precise and comprehensive. On the other hand TypeScript doesn't (need to) care about things like pointer ownership, so often there's much less to worry about.
Am I missing something?
Why the hell are people upvoting a “buy now with discount code” page?
This is using HN as an ad platform.
1. HN, in my experience, is an extension of ycombinator. Since it's this guy's entrepreneurial thing, I think that's relevant.
2. I'm interested in Rust, though I've never used it.
3. I'm the target demographic for this.
edit: formatting
Yep, that's how most people seem to feel about it before they try it.
> You'll be charged US$62.01, including US$12.40 for VAT in <my country>.
It is off topic for the book but I want to add a yew.rs frontend. I call it the PAY stack (Postgres actix and yew).
Anyone up for giving me an elevator pitch that it would be? Or, also interested in confirmation that it wouldn't!
1) Latency/peformance/concurrency/server costs
or
2) Extreme correctness
If neither of these things is a current or anticipated problem for you, it may not be worth it (though you could always do an exploratory project to find out!). Make no mistake: like any engineering decision, for all its advantages it does come with some costs.
* exhaustive pattern matching
* Result and Option types
* builtin first class unit testing ability
* builtin first class documentation ability
The enforced exhaustive pattern matching tells you “hey you didn’t cover all cases.” Result and Option types make error handling explicit and ergonomic—the notion that each function can either succeed or fail is supported at the language level (in other languages, it’s up to you to ensure you’ve covered all cases and have thought about what “success” and “failure” mean for each function.
Testing is “built right into the language,” it’s frictionless. It’s super easy to write unit tests. You don’t have to canvass and vet a ton of testing frameworks to get going.
Documentation is easy, fun, and quick to write. So, now combine all the above with the culture of detailed documentation Rust has and now you’re really thinking through your codebase—you can use writing docs to tighten up the interfaces, seamlessly transition to writing a test that enforces some invariant, leverage “compiler driven development” to rinse and repeat this process.
The strongest reason to use Rust is that you’ve detailed the requirements for a piece of code and determine Rust is a better fit than LangX. A weaker, but still valuable reason to use Rust is to experience the discipline and idioms Rust enforces/enables and then take that experience back to your native language (I.e. exhaustive matching, which of used in certain ways can be a kind of “proof by cases” that your code does exactly what you expect, then you can easily inspect what you expect via tests).
For me that means you won't get to Production (at least not starting from Zero) since these are concept that make Rust truely different from other language and without properly grasping those, you will run into hard to understand errors and code that won't compile.
It also seriously affects how you structure your code in general.
There are plenty resources that do properly explain these concepts but in the end you will just need to get it in your system, which is what I'm currently struggling with. The flow of code as I'm used to writing in C, C++, Python or Go simply won't work for Rust
I really appreciate how the author keeps a pragmatic approach, by copy-pasting code snippets found on the libraries docs and, at the same time, goes into details and explains every little line of code, without overwhelming the reader.
Just a quick (design) feedback: I personally felt like those blue boxes used for pull quotes draw a little too much attention. I wasn’t sure “how” I was supposed to read them at first: is this a “quick tip” box? is this something important I should pay extra attention to?
When I was first trialing Rust I didn’t feel like I was missing anything by using free resources. The Rust ecosystem is moving fast enough that a book might only be relevant for a short while anyway. You’d end up having to read the latest documentation to use updated packages anyway.
This may not be a bad way to spend a book stipend if you have to buy something, but don’t internet the existence of this book as an indication that it’s necessary to penetrate the world of Rust backend services.
Using a systems language where it’s not required is just burning time and money.