Hacker News new | ask | show | jobs
by Dowwie 1572 days ago
For those who have been considering Rust for your next web dev project, it is a great time to give it a try. Actix Web is a mature offering with ecosystem, community and learning materials, including a few major book publications in the pipeline.
3 comments

I love working with rust for personal/toy projects, and I've been really itching to find a good use case on the web!

The last I investigated, there were many gaps where one had to "roll their own x" - as compared to something like Rails, which comes with a lot of functionality (and has a huge ecosystem of libraries to support it for stuff that's missing).

If you're familiar with Rails as well... can you speak to what I'll be missing when I work with Actix? Some things I know I take for granted are the ActiveRecord ORM, the session framework, the availability of gems to handle various authentication systems, caching, and a ready "out of the box" browser testing framework.

I know I shouldn't expect anything close to what Rails has to offer, and for hobby projects I don't have an issue with that, but I'm curious about just how far things have come.

There won't be a Ruby-on-Rails or Django like experience with Rust for a long time.

My response has gotten fairly long, so I think I'm going to turn it into a blog post. Here's a start:

I can't tell you how far you may get with the ORM libraries currently available as I moved to parameterized SQL libraries some time ago. Others seem to get on just fine with diesel, the most popular ORM/query builder. It's very fast, too -- nearly as fast as the plain old paramaterized SQL libraries -- but parameterized SQL libraries are much more popular (at least 3 times more popular) if we were to just look at download stats. You can go a long way with those and roll a custom query builder helper should the need for it arise. There are a few popular async connection pools, deadpool being my favorite.

Actix Web had a fine session management solution, but it has recently gotten a lot of attention lately and a PR awaits for merging with v4 that makes it even better. Actix Web has a strong session management solution, with support for Redis already built.

In terms of authentication, there's a popular jsonwebtoken crate that handles all of the JWT related functionality. There are plenty of strong cryptographic hashing libraries for argon2, bcrypt or whatever you're looking for. There are oauth2 clients. There are hashicorp vault clients. Or, you could roll these clients yourself, if you were so inclined to. There are also systems such as those maintained by the ORY community, written in golang, that have intuitive APIs and can serve auth related purposes. Many options to choose from with regards to authentication and authorization.

There are at least two viable async http clients to choose from. You'll use either extensively.

In terms of caching, there's a pretty good redis client. There isn't a "cache aside" (aka cache-else-db) library in open source, yet. People roll their own and don't seem inclined to share them just yet. :)

Graphql story consists of 2 viable libraries, async_graphql and juniper. Neither is exceptionally better than the other, but async_graphql seems to be leading.

Testing in Rust is pretty good but relative to Python or Ruby it has a lot of room for improvement. I've learned a lot over the years through trial and error and would like to blog about it. The one situation that is difficult to handle in Rust involves end-to-end integration tests involving calls to 3rd party api's. If you wrote your own client, then you can design the client so that you can control URL's, but if you're using open source clients, you may not be able to control those URL settings. This makes it more difficult to route requests to mock servers. Using dependency injection and mock types is what people are doing at the moment to address this issue. There is a nicer alternative, and that involves running a mock server that handles request interception. This is a rather impressive category for open source development and anyone reading this should take a look at what people have done in go, node, etc.

Really great summary, thank you!
Also check out Axum, which honestly in my hands has the best ergonomics, and is built on top of the Tokio ecosystem.

https://github.com/tokio-rs/axum

I always wondered if the safety and correctness that Rust offers is needed in a CRUD application? Do you think it pays off to work in the extremely strict system where you can't just cast to any and move on with your product features?
This is something I thought about a lot before switching to Rust at Svix[0]. What I came to realize though, that there are no "casts to any and move on", but rather there are silencing real bugs and praying you won't hit them.

Almost every time I've ever tried to silence the type checker (in any other language) it resulted in a bug.

Does it make you slower? Definitely at times, but it's mostly OK, and the code is pretty damn clean. You can check out our repo to see how it looks[1].

Edit: we don't use Actix, we use Axum, but the same holds for Actix too.

[0] https://www.svix.com

[1] https://github.com/svix/svix-webhooks

+1 for Axum. I recently moved [1] the Rust Playground's backend to Axum and have been very happy with it and the team producing it.

[1]: https://github.com/integer32llc/rust-playground/pull/777

> Almost every time I've ever tried to silence the type checker (in any other language) it resulted in a bug.

What does it even mean? You can't silence a type checker.

You can in JS/TS and Python. Technically you could even do it in a language with a fairly strong type system like C# or Java by casting to object or dynamic, though the code using the cast object after that will probably be very unidiomatic.
Can't you cast to any in Rust as well?
To expand on the others, unlike in TS/JS, Python etc you can cast to the `Any`-trait but cannot use it as if it is any value, you need to convert it to the type you want and then handle the errors. A function that accepts `&str` will never accept a `Any` value, unlike in TS where you can override the type checker using `as`. Worth noting that TypeScript's type system is unsound [0] too.

[0]: https://www.typescriptlang.org/docs/handbook/type-compatibil...

No. Rust does not have the equivalent of Object or void *.
There's the Any trait [0]. Not sure if this is exactly like void* in C, but this doesn't look particularly ergonomic to use.

[0] https://doc.rust-lang.org/std/any/index.html

Exactly what the sibling comment said, you can do it in many languages. Also, the comment I replied to suggested just that ("cast to any"), which is the context. :)
If you're writing software that will be long-lived with multiple developers, the safety, correctness and strict type system is absolutely necessary for maintainability and refactoring purposes alone. Even for a CRUD app (maybe especially for a CRUD app as you're marshaling objects of different types around, usually), and even when runtime speed is a secondary concern.
But a higher level language with a GC and good types would be more productive , I would suggest to also check the ecosystem not only the language, the access to documentation, and developers (if you need to build a team). From my experience big projects suffer not because of the language but because of bad architecture caused by inexperienced developers.
I don't disagree with the ecosystem argument, nor the argument that poorly thought out architecture is a big cause, as well. I don't think having a GC de facto makes you more productive and that's worth arguing about itself.

My point was that having the ability to perform "fearless refactoring" to improve architecture and adapt to changing needs of the software needs ruthless type system support to catch the non-obvious areas that you just broke and make sure that API contracts are still correct and to make sure you're not deserializing random stuff into `interface{}` or worse, `Any`, `Value` or `void *`.

We had strong types and languages that allowed that before Rust, I do not understand why would people push Rust everywhere and not focus on the stuff it is actually good at like low level stuff where performance and safety is needed.
> the safety, correctness and strict type system is absolutely necessary for maintainability and refactoring purposes alone

Erlang would beg to differ, and it has been continuously running on systems with uptimes longer than rust has existed.

I work on CRUD for life.

Rust is GREAT for that.

Is not the safety (borrow checker) but the combination of: Bare structs, enums, pattern matching and some traits (like Into) that make a breeze modeling business rules.

Basically any ML derived language, with the added value of automatic memory management.
Any web (or any RPC) facing app that needs high performance and security should consider using Rust frameworks (or go). They are far more efficient than the interpreted languages and 1 server can usually replace several in such cases. It's well worth converting 1 or 2 "small" microservices that require "significant" server side "work" in comparison to interpreted stuff like rails and python.
Once you acquire the capabilities to do the work efficiently with Rust, it's hard not to use it even for smaller CRUD applications. You'll have a stronger, faster system that uses resources efficiently. It's a good tool for team development efforts, too, as you refactor each other's code and quickly find out what requires fixing (way before releasing to production).
> Once you acquire the capabilities to do the work efficiently with Rust

The problem is getting to that point.

I only tried Rust once, two years ago. The type system was fighting me all the way: the types different libraries were using were incompatible with each other, there was no obvious way to properly convert between them, finding proper signatures and behaviors was pain as many things were in traits or macros etc.

A lot of this comes down to tooling and ecosystem maturity, so things may have improved.

I had a few really strong reasons to push through, but it is understandable why others decide not to. It hasn't been cheap. I ate sunk costs plenty by adopting too early. I get angry at how much better the ecosystem and resources are now than when I started!

This aside, you'll still battle with the compiler. As to what you're battling depends on what you're building.

This.

I still write Python for quick scripts, but if it's gonna be a page or more of logic I give up and write it in Rust. Compile times for quick scripts, in practice, don't really bother me, and cargo is fantastic for "just work please".

I think there are quite some macros that accelerate a CRUD creation app, for example, take a look at sqlx with ormx: https://github.com/NyxCode/ormx