Posted on 11/15/2022 9:09:15 PM PST by Cronos
Pitchforks down, please! I felt your anger as soon as you clicked on the article. I have nothing against Rust — I prefer it for various use cases. All programming languages are means to an end. In my case, Rust simply did not cut it, and I had to gut the project and rewrite it in Golang.
The project is a simple backend webhook service for Hasura. If you aren’t familiar with Hasura, it’s a wrapper around a Postgres database that gives you an instant GraphQL API. It’s very handy for people like me, who are solo developers building out a passion project: writing out every REST endpoint or GQL resolver takes a ton of time, and it’s mostly the same CRUD operations on every model. This kind of falls apart when you need some more complex logic — for that, Hasura allows you to map a GQL request to a custom webhook. I use this for S3 file uploads or authentication, to name a few.
Problem One: Dependency Injection Woes The Rust dependency injection story is a interesting one. As soon as you ask for a concrete type, i.e.
fn do_stuff(db: &Database) {
db.create(Stuff);
db.read(Stuff);
}
You have to pass an instance of Database into do_stuff ; no exceptions! You can’t “subclass” Database (subclass is literally not a concept in Rust). So, if you were a programmer who didn’t test their code, then this would be perfectly fine; in reality, you’re really ever going to have one implementation of Database, so there’s no reason to make this function take anything other than Database .
And us testers? We have to rewrite the function signature. Database needs to be a trait, and then we take that trait and we implement it on our mock objects. Okay, that’s not so bad. In fact, I’m doing essentially the same thing in Golang; so where does this start to actually fall apart?
Problem Two: Async Traits In Rust, you have easy async, you have easy traits, but having async traits is a little difficult. Most examples I’ve found in Rust of async traits use the async_trait macro. That’s very helpful, and I was using it and was a happy camper.
Here’s a summary of my journey so far:
1. Write a struct; be happy.
2. Write a test; realize you can’t dependency inject. Be sad.
3. Convert the struct to a Trait; be happy.
4. Dependency inject to my hearts content.
5. Use mockall crate to generate mocks automatically. Be very, very happy!
6. Have an async http call I need to make.
7. Realize Async traits need a special macro.
8. Realize that that macro does not work well with Mockall.
9. Be sad :/.
In hindsight, this problem has a solution. Perhaps I should have given it another go before I made the switch to Go, but at this point I was already kind of frustrated…. by the next point.
Problem Three: The Nail (Compile Times) in the Coffin (Containers)
Blah Blah Blah rust has bad compile times. We’ve heard it a thousand times; you can’t have a language that does everything and no downsides. That’s just impossible — Rust’s cons are that lifetimes are difficult to wrap your head around, and its compile times.
I have a nice, beefy laptop — the M1 Mac is a workhorse. Absolutely no problems with compiling rust on my Mac. Typically, when I write servers, I develop locally and make sure that each time I make a change, I reload the local server and test the feature very quickly before I commit to a real unit test. That requires lots of compiles between trials; which is fine! Again — no issues with compiling Rust on my Mac.
In a container though? Forget about it.
The easiest way for me to orchistate many local services without having to worry about running npm run in each service (Hasura, web hooks, mock s3, mock oauth server..) is to have a docker-compose.yaml that runs all of these things. That means, typically, it’s a docker-compose.dev.yaml because I don’t actually use the docker compose to deploy. only to develop locally. This has the side effect, though, of my Rust code needing to be compiled in the container — since:
1. Non-negotiable about auto-hot reload.
2. Non-negotiable about developing inside a container.
I have two options: either throttle my whole computer by spinning up a massive image for Rust to compile in, or deal with 3+ minute compile times. My dev cycle grinded to a halt, and I felt incredibly unproductive. I tried changing my workflow to write the code and the test before manually testing, or to not use auto-hot reload, but I felt cripped. I just couldn’t do it.
I finally bit the bullet and switched to Go. You will be missed, Rust: I greatly enjoy writing Rust code. I find it beautiful and expressive, utilitarian yet elegant.
If I’m writing local helper libraries, performance sensitive code, any backend services I don’t need to run in a container…. Rust is the first pick. Especially if I don’t need to convince anyone else to use it.
I would generally prefer C, but that’s more , I suspect, from habit. Rust has innate garbage collection.
I think of C as for when you need very low level efficient code, and C++ for when you need a *lot* of very efficient code all in one language. Rust covers almost all of the situations you would use C today, and a lot of the C++ cases as well. Go has some of the C++ cases, but it’s pretty similar to the C# or even Haskell cases.
As a guy taught how to design languages, I find this interesting.
The critical thing you really do is define the problem you want to deal with and then select the methods, tools, resources, and supplies to do that task in the time frame, within the costs, and deal with the inevitable failures
you will encounter.
That is not coding.
It is called engineering.
A skill most all “High Tech” coders don’t have a clue about.
tech ping
But I haven't been wow'ed by any of the newer languages, Rust or Go among them, perhaps for the same reason that I haven't gotten around to learning Italian -- I don't have an application to write that demands it.
You need software engineers and architects to design the flow and hld and to some level lld, but for the grunt worm it may be better to use coders as well, there are more of them and cheaper. And invest in great qa, preferably pessimists who believe there is something wrong with the code.
Impressive
Thanks. I deeply enjoy programming. I’m very fortunate in that for most of my career I’ve been paid to do what I would otherwise do in my spare time as a hobby. That’s a blessing.
Long retired Engineer….
Rust, Golang, Hasura, Postgres, GraphQL API, REST, GQL, CRUD, S3, etc etc
But of course..... easy as pie.
I can barely do html/css to create a simple webpage.
Peruse later.
“But I haven’t been wow’ed by any of the newer languages, Rust or Go among them, perhaps for the same reason that I haven’t gotten around to learning Italian — I don’t have an application to write that demands it.”
I agree with all your comments especially the one above. In many lustrum of programming for money I have yet to find an app that can’t be done most efficiently in ‘c’ and (gasp!) COBOL. Web pages in ‘PHP’ work well when supported by ‘bash’ and ‘c’.
In reality the project should define the language(s) needed instead of forcing a language on a project.
I don’t have a clue what this guy is talking about, but I found it an entertaining read. Lots of people who think they know how to write could learn something from him.
“Rust has innate garbage collection”
That’s expensive at execution time. Otherwise all languages would have it.
Shoot, I even dip into assembler from time to time whenever an interrupt handle must be really fast.
i prefer c/c++ for my server backend responding to network requests or to easily enable pub/sub strategies... all with an eye on performance.
the whole stack these days seems to be tending towards:
( c/c++ backend ) <—> ( protobuf messaging ) <—> ( javascript based web front end )
native frontend code seems to be going the way of the dodo, with chromium clients being the portable foundation for most native dev
thoughts?
I've written quite a lot in assembler as well, but so much in the last thirty years or so.
COBOL! Wash out your mouth!
Write so that you don't create garbage.
Disclaimer: Opinions posted on Free Republic are those of the individual posters and do not necessarily represent the opinion of Free Republic or its management. All materials posted herein are protected by copyright law and the exemption for fair use of copyrighted works.