A taste of pavex, an upcoming Rust web framework
lpalmieri.com
lpalmieri.com
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.
I’m the opposite, I don’t find much need for “dependency injection” frameworks. It’s trivial to do DI without frameworks.
I’ll believe in rocket when I see releases being cut. Until then I strongly recommend no one touch rocket.
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.
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.
Is there anything else missing that you heard of?
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 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.
Generally this kind of “too clever” programming tends to fail in creative ways, especially over longer periods of time.
As long as the output code can be verified by tests, I don’t see how this would fail in „creative ways“.
(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.
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.
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.
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.
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.
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 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.
Still looks very verbose. But then asp.net has had a decade, and this project is new.
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.
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.
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’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?
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.
The pavex api looks way messier, but maybe it’s cuz I came to rust as an express and flask user
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.
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.
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