64 comments

[ 4.6 ms ] story [ 112 ms ] thread
I'll definitely check this out, I have a microservice that I have rewritten in rocket, warp and axum that I'll port for comparison.

Rocket was nice but has stalled, I read the author has health problems to focus on, so my go-to framework is axum for now.

What I miss the most in the rust ecosystem compared to the kotlin+spring boot stuff I do at work is the dependency injection, I haven't found anything I like much in rust.

FYI Rocket has revived, and I heard recently he (Sergio) has made plans for a Rocket foundation so that the development team can be expanded and there is no longer SPOF
I respect Sergio but I’ve heard that like too many times in the last 3 years. Every few months it’s the same excitement whenever some activity is spotted on the repo. It always ends the same way - stalled yet again. The goodwill earned has been squandered.

I’ll believe in rocket when I see releases being cut. Until then I strongly recommend no one touch rocket.

So, the last time I asked on #rocket they told me the release was basically done, but they were not going to move past RC until the documentation was completed, which seems stalled.

Is there anything else missing that you heard of?

I’ve heard that story many times. Maybe it’s true this time. I’m not going to stick around to find out because I got burned before.
Can you give a bit more detail on the burning bit? Just want to know if there's anything missing in particular from the 1.5 release besides the docs.
Why do other people need to be working on a library for it to be useful to you? Can’t you pitch in?
No I actually can’t because during these hiatuses there were many PRs submitted. These weren’t merged in because the author didn’t review them.

Why does HN have this urge to write this template comment of “oh it’s free software, no complaining no matter how bad the governance is, you should pitch in even if there’s no conceivable way to do so”.

Like, did you even bother learning anything about rocket before writing this comment? Did you feel very virtuous when you wrote it?

I use rocket myself. There’s nothing huge missing, and nothing stops you from merging those PRs into your own repository (public or private) if you want them. How then is there no conceivable way to pitch in?
It's weird seeing this sentiment when Sergio is on matrix answering questions all the time and the contributor graph for alternatives like Axum is basically identical, one person making all the commits, another person doing a fair amount of work, and the rest are drive-by PR's.

Seems more like an entitled open source user issue rather than a repo issue, he's never slept on any security notifications as far as I know.

The 0.5 release was a big re-write, it takes time. I likely will still use 0.4 in future because not having to mess around with async is nicer and it's still perfectly fast enough for many usecases.

The issue is that in the past rocket author went missing and no one could take the lead to continue the development, axum in the other hand is under tokio org, so the community could take the lead in that regard.

I loved rocket when I first learned about it, now two years passed and it stalled because no one could pick up the project, there were (I don’t know it there still are) a lot of PR's from the community that were waiting for someone to review and merge it.

But if rocket is being migrating to an org, kudos to the project and hope it continues to innovate like in the past.

(comment deleted)
I skimmed the post. Looks like asp.net approach of registering all dependencies up front (with various lifetimes/scopes).

Still looks very verbose. But then asp.net has had a decade, and this project is new.

I loathe this approach. Declarative architecture is incredibly hard to debug and understand.
There's nothing declarative in asp.net.

You specifically define services and dependencies in one place in code. It goes something like this:

   public void ConfigureServices(IServiceCollection services)
{ // Add framework services.

    // Add application services.
    services.AddTransient<Type, ClassThatImplementsType>();
    services.AddScoped<Type, ClassThatImplementsType>();
    // etc.
}

So when dependency injection happens, you know exactly which class is used, and where it was added.

That's declarative. You're injecting code using the type system. You aren't directly calling any constructors with those classes -- they're added in code that you can't easily put a breakpoint in.

The result is that you're looking at a class with a dependency of "ISomething" and you can't click through to the class. You have to go back and find where the services were configured (which sometimes isn't intuitive, depending on how the original person wrote the code).

The worst is when there are entire behaviors added this way, and you just have to search your code base for keywords to figure out what's happening. It's awful.

.NET is a great ecosystem, but the ".NET way" of doing the architecture is insane and not ergonomic.

> The result is that you're looking at a class with a dependency of "ISomething" and you can't click through to the class.

You can. Jetbrains' Rider will happily show you the implementation (Cmd + Option + Click IIRC)

> The worst is when there are entire behaviors added this way, and you just have to search your code base for keywords to figure out what's happening.

Not in ASP.net in mu experience. But true for Spring in Java.

In ASP only the classes you declare in the constructor get injected. Only the classes registered in ConfigureServices get injected etc.

All in all ASP.net has surprisingly little magic compared to many other frameworks.

That's not to say it's without warts. There are definitely WTF cases and places where you can't properly stop a debugger.

I was with you all the way until the api example.

That doesn’t look ergonomic, it’s verbose AF, and the ugly use of f! is (like most crates that lean heavily into macros) going break tooling like the intellij rust plugin.

Does rust really need another web framework?

Does it need a compiles-to-rust meta language that means you cant easily see the actual rust code you’re using?

Does this feel ergonomic enough to make it worthwhile the downsides of the first two?

Hm.

Yeah I don’t get it. The Axum example is pretty easy and the actix counterpart would be easier.

The pavex api looks way messier, but maybe it’s cuz I came to rust as an express and flask user

It’s Java written in Rust. :shrugs:
(comment deleted)
Metaprogramming/macros are my biggest gripe with Rust. Sure, they're cool, but they're annoying when they're thrown all over the place. Especially proc-macros. You have no idea what it's actually doing unless you hunt it down.
It has the worst ideas of the frontend world (transpiler) and the worst ideas of the backend world (verbosity of c#). I'll stick with Axum, thanks.
(comment deleted)
Is there popular Rust web server which uses just threads with no async stuff?
Rocket.rs I believe
Rocket is moving towards async as well
Rocket is not moving anywhere right now. 0.5 supposed to be async, but it's delayed more than a year because maintainer doesn't have time.
You don't have to use async in Rocket v0.5.
I suppose async web servers is the poster child of async, and drove a lot of the development. However, then _everything_ went async, adding the cognitive overhead of dealing with Rust async throughout the ecosystem, whether needed or not.

In short, it still feels like a leaky abstraction: marvelous to look at working code, but errors (both compile and run-time) expose the underlying machinery underneath the syntactical sugar.

This is probably heresy, but as a former Rust dev I'm enjoying web-services in Go more these days.

I love Rust but I kind of agree. It’s really, really rare for a web service to need the low-level stuff Rust provides. A garbage collected is going to be just fine.
I hated async for months. Refused to touch it. Recently I tried it again. It (seems) way better now. Just avoid weird libraries like diesel & rocket, and everything works just as expected. I even wrote some generic code with async and surprisingly it works without me having to get a phd in type theory to please the compiler.
And if you want to make it async anyway, take a look at the abstractions a garbage collected language like Haskell can get you. (But well, the ones Go provide are probably more than enough already.)

Rust was not made for web development, and the fact that people keep creating frameworks and keep deciding they are good only shows how bad the usual practices are around the web.

(comment deleted)
There are other applications, e.g. trading, where async is a godsent (monitoring hundreds of financial instrument for changes) but any sort of GC is prohibitive.

And where you also need the fine grained control that Rust provides.

E.g. halting all async tasks immediately while keeping a single core free to execute a trade as fast a possible.

Is a thread per request scalable? Aren’t most of those threads just waiting for I/O? I guess thousands of concurrent requests via thousands of threads isn’t a big deal.
You mean at the application layer?

There never was any problem scaling things at the application layer. You can use almost any architecture there without issues. All the bottlenecks are elsewhere.

It's reasonably scalable. Operating system can deal with thousands of sleeping threads just fine. And I don't need more than that (on a single machine anyway).
Yes, you can run absolutely massive systems with very high throughput and high concurrency using only OS threads and "normal" synchronous programming, if done correctly. However it's generally a pain in the ass because most languages often have their own threading models that may have their own limitations or interaction-with-the-OS quirks, while in C or C++ you're typically on your own for like, all your dependencies including trivialities like thread pools and stuff. And all newer languages tend to instead design their I/O ecosystems around asynchronous stuff. So you have to choose your poison. But handling massive systems always requires a bit of special care anyway.

You will often encounter many other bottlenecks that hold your system back. There are other concerns like latency and utilization to care about. But assuming a purely "compute/IO" driven data plane, yes, you can scale synchronous APIs with threading quite far.

async is pretty efficient, no reason to ditch that.

It's just a pool of threads being used on demand.

The source of "complexity" according to the author is in using traits to make sure the middlewares are defined correctly.

Looks interesting and I am totally behind this for exploring new design decisions.
Ugh, Dependency Injection Framework. This will not be popular.

The blogpost itself is already shown what’s wrong with pavex. Adding so much concept and extra code just to be able to instantiate something. $1 abstraction cost for $0.05 problem.

(comment deleted)
rust ecosystem is very JS-like in this regard, having spent the last few years working with it.
Yeah, so many more leaky abstractions that will be hard to debug. Not even sure why? You're not saving anytime by doing it this way. Axum already has good DI that is somewhat ergonomic in rust (well, I hate how extensions aren't checked at compile time like in warp...)

I think the solution to "rust doesn't have DI like C# and Java" isn't trying to make DI like C# and Java (you won't be able without very heavy runtime cost), but make a DI that works well in rust and re-adjust yourself.

You identify a problem then create a leaky abstraction that is hard to understand to solve it and then call it ergonomic.
(comment deleted)
Not sure if author is the same as OP (I’m leaning towards not?), but if they are…

I’m curious what caused you to go down the route of transpiling? Do you think that this methodology will be a core facet of the architecture, and will it be able to be changed when rust gives you the tools you seemingly want? Do you think that the transpiling will be annoying to debug and update as rust continues to mature?

The answer to help beginners understand highly generic rust code is not more abstraction and magic.
(comment deleted)
Very interesting to see movement but personally I found the debug_handler route picked by axum to be easier to understand and work with.

Transpiling means you end up with two codebases and having to think about more abstractions.

I sincerely hate transpiling from all the work I've done in node.js / babel and other similar devilish spawns

(comment deleted)
I like the idea!

I disagree with other commenters here that dependency injection is not useful. I think it doesn't come across well in toy examples, but in my experience it is very useful for large services, which otherwise tend to accrue a big rats nest of instantiation in the startup code, which is difficult to modularize. I haven't built a service like that in Rust yet, so maybe other patterns work well, but I have noted these big instantiation entry points in rust code that I've read, so I'm skeptical that it is a solved problem.

I do agree that it introduces a significant legibility and debugging burden, but I think that can be significantly better with compile time DI. However, I think it does require good tools that would take quite awhile to be built for an immature framework like this.

But the thing I'm most skeptical of here is the implementation. Is there not enough information available in a normal procedural macro to figure out the dependencies, without using this trick with the rustdoc data? That approach seems very cheesy and likely fragile to me.

I'm always wary of tools that as an extra compilation step, because of the added complexity.

(Look at the common C workflow: cc was too hard to use on its own, so make was created; make was too hard to use on its own, so autoconf was created; m4; ...)

I hope the user experience makes it worth the complexity.

If you haven't also check out Zero to Production in Rust, it's an excellent book by the author of this framework.
FYI: This framework uses rust doc-comments for essential functionality. It’s basically using the documentation tool’s JSON output programmatically during compile time.

Generally this kind of “too clever” programming tends to fail in creative ways, especially over longer periods of time.

It’s not using doc-comments, but compile-time reflection — it’s just that Rust‘s only reflection output is JSON. That doesn’t render the preposition of using a reflection API for transpiration invalid however.

As long as the output code can be verified by tests, I don’t see how this would fail in „creative ways“.