Maybe it will be better now after the 0.5 hump, but in general the development history of Rocket is something that dissuades me from using it when compared to e.g. Axum.
So, it wasn't actually lagging, it had that async for quite some time... But yeah, being in a "release candidate" did scare people off.
It’s not like users can’t run release candidates in prod, but there are many reasons not to, and good policy guides against this.
I used to be an ardent supporter of Uber’s h3 geospatial library. But we’re running into almost exactly the same issue. Bug impacting us we’re fixed in 4.0 and it’s been stuck in release candidate status for over 2 years. That’s not tenable as a user who is trying to get things done. So now we’re migrating off it entirely.
In some places we were told to never use libraries that weren't 1.0 - obviously that would be bonkers in the rust world. In these things you really have to use your own judgement...
I don't feel like their decision to make documentation a release blocker shows disrespect for the users. They were repeatedly clear that it was safe to use and feature complete. They did note the shift from a single man project to a foundation governance took longer than expected and was part of the release as well, but, you know, non-code stuff like that also takes longer.
The author mentions flask but looking at the "What we need section", I don't think flask covers those. I hate Djago with a passion but if those are the requirements, I think Django is the one that closely resembles what the author is describing. So Poem is not a good candidate either in that regard. Poem is all in all, something that closely resembles FastAPI, which is actually a complement. I've tried half a dozen rust web frameworks and they all come with a ton of boilerplate fiddling with the initial setup. Which is a problem if you want to get things done fast. In that regard, poem nails it. Yes, actix has a considerably better performance but unless you are aiming for sub-30 millisecond responses, then actix is not what you should be looking at to begin with. Also if you crave a Pydantic, there is a crate that sort of does that for you: https://crates.io/crates/poem-openapi
You can do dependency injection in Rust to share connection pools just like you would in Java, and it's super simple to write threaded background tasks.
Google gave a talk recently that said they measured Rust developers as no less efficient or productive than Go developers [1].
It's a fantastic language and you shouldn't ever limit yourself to systems programming with Rust. It can do so much more than that.
Otherwise it's just stating that they assign the languages to categories of problems they are equally efficient in.
> "We have at this point rewritten a large number of systems [..] have some very concrete things that we can say"
> "When we've rewritten systems from Go into Rust, we've found that it takes about the same sized team about the same amount time to build it. [...] no loss of productivity when moving from Go to Rust."
> "We do see some benefits [...] we see reduced memory usage in the services that we've moved from Go. [...] We see a decreased defect rate over time in those services that have been rewritten in Rust"
> "Within two months about a third of the folks were feeling as productive, within four months about 50%."
> "People do indeed feel as productive in Rust as they do in their original language. C++, Java, Python, Go."
> "One of the biggest latencies is code review time [...] How hard is it to review code in Rust? [...] A little over half said that Rust is easier to review. The most incredible question [...] The confidence that people have in the correctness of the Rust code that they're looking at. [...] 85% of people believe that their Rust code is more likely to be correct."
A rewrite should be much faster than the original. Much of the original development will have had changes in requirements, refactors because of better understanding the problem domain, best structure for the code etc. A lot of that can be just 'copied' into the new system. The developers know exactly what to do.
To complete this experiment they should have also re-written it in C# or java and then compare with rust.
Having written services in the Google style for years at a major fintech surrounded by Xoogler peers, systems design starts with a design document. You capture requirements upfront and solicit buy-in from the stakeholders. It's nothing at all like what you describe. You design the API and systems upfront without code (apart from capacity testing) and actual implementation happens quickly.
If iteration time was some "gotcha", Google would have made that footnote. That's not really how service development works in companies of this scale.
Translation time from design document to service is the same for Golang and Rust.
Likewise on CLR with F#.
Having a rust alternative to something like libmicrohttpd would be nice and I would use it.
Not having to learn something like PHP? Do we not count learning C++? I would guess most people would agree learning PHP + Nginx is going to be a lot less complicated, which will be preferable for people just trying to build something. I'm not sure dropping down to such a low level is something most web developers will have to deal with.
I think that you find it easy speaks more to an already existing familiarity with C++, not that it would be a path of less resistance.
No, because they presumably already know it. They wouldn't be using it otherwise.
Neither is the intersection between embedded programming and web programming.
But either way, if something like Django, .Net, or go is an option, Rust is probably a bad idea.
Rust is a high AND low level lego due to type composition and ability to directly address memory with no hacky garbage collection required.
Garbage collection is a bad idea when your state machine is basically a stateless request response cycle that can fit in n stack frames and has a defined lifetime ending in the response.
Interpreted languages calculate runtime code paths on an ad-hoc basis and are subject to bugs that a binary executable will never encounter.
One can objectively state that Interpreted languages and their virtual machines are LESS DETERMINISTIC than binary executables whose runtime code already exists at compilation time.
Of course, shared libs and other things that violate the spirit of the above can wreak havoc with even binaries, however the executable is a finite artifact.
I kinda agree with this, but the speed of a language like Rust changes so much of the mental calculation. Like in Python/JS/Ruby you try to offload as much work to the database as possible, because its so much faster. In Rust, you might not need to do that, because its such a fast language.
Rocket is maybe the closest? But it's not as batteries included as any of the frameworks I've mentioned.
Sure, some people like choices and tinkering, but in many settings it’s much more productive to have the choices curated for you.
“But what if you want a different X instead? It might be Y% better!” is the typical comment, which ignores that the integration itself provides enormous value. Discrete libraries may be best of breed individually, but may be a heap of garbage if they don’t fit together smoothly.
I love programming in ASP.NET 8 specifically because I never have to think about whether a templating system will play nice with authentication, injection, routing, or anything else.
What sets these projects apart from frameworks like leptos is that there's a CLI guiding you. That's what I love about Symfony, and what I consider the 'lazy' part.
You don't need a router when you have pattern matching (just split the url and match on static and dynamic vars however you need)
Auth is typically DIY in any language, or SaaS like Firebase/Auth0. It's not a language or framework problem, necessarily
CSS/JS tooling makes no sense for many frontend Rust frameworks like Dominator, which is in Rust (not JS) and has its own approach to styling that works for it (e.g. attaching styles to signals, so they change on the fly)
I get what the author is saying - in fact I've been around the block a couple times solving all the different points in the article and it is painful. For example, see https://github.com/dakom/dominator-workers-fluent-auth which does a lot of the stuff here on the Cloudflare workers framework (also adds another wishlist item - localization with Fluent)
A "batteries included" full framework that does _everything_ is nice to have, but many real-world projects will want to replace those opinionated decisions and go the DIY route anyway. Rust is more than mature enough, even on the frontend web, to tackle all of that - if you know where to look (wasm-bindgen, etc.)
Web frameworks allow for much more: URL redirections, specific management of append-slash and case-sensitive URLs, complex regex matching, etc.
> Auth is typically DIY in any language, or SaaS like Firebase/Auth0. It's not a language or framework problem, necessarily
False. Django, Laravel, Rails and batteries-included languages have really good auth management. I personally consider it a gigantic mistake in 90% of orgs to outsource auth to external parties.
The ability for experienced web devs to just hit the ground running and have 10 basic CRUDs running in a single day because they don't have to deal with this needless complexity is simply amazing for small businesses.
Erm, just return a new url after the match? Get fancy with state machine like enums? Rust has everything you need here, not getting why you think this requires a framework.
> Specific management of [...]
again, match for that, map your url parts, whatever - it doesn't need a 10,000 pound gorilla when it can be done in a line or two of code
> complex regex matching
erm, regex crate?
> Django, Laravel, Rails
Yup, those are valid choices. I said it's not a framework problem necessarily.
Not a one size fits all sorta problem.
Wordpress is also a fine choice if your business is knocking out new sites for a new client every month.
But, if you're building a single product over the course of a year or two, it's not the end of the world to spend a couple weeks rolling your own auth and hook it up to transactional emails and everything else. It's just one small problem to deal with, not major in the grand scheme of things. YMMV
Because you're reinventing a wheel that doesn't need reinvention, and the most likely thing is that you will neither reinvent it nor pick the best library that an opinionated framework with hundreds of eyeballs has.
> again, match for that, map your url parts, whatever - it doesn't need a 10,000 pound gorilla when it can be done in a line or two of code
Sufficiently large and complex websites will have that need.
> But, if you're building a single product over the course of a year or two, it's not the end of the world to spend a couple weeks rolling your own auth and hook it up to transactional emails and everything else. It's just one small problem to deal with, not major in the grand scheme of things. YMMV
No individual problem is large, but it is objectively a dozen little problems, all with a nontrivial chance to blow up into larger problems.
Code reuse and frameworks exist because, unless you've been doing web development for a long time, you _will_ run into issues that have already been solved.
From my example above, check out https://github.com/dakom/dominator-workers-fluent-auth/blob/... and follow it through to https://github.com/dakom/dominator-workers-fluent-auth/blob/...
Do you see that you have static and dynamic parts matching, multiple variable capture, etc.?
I'm not talking from a theoretical perspective. I'm showing you an actual example of a fully baked auth system that does everything on the wishlist (and more) and has no need at all for a router because Rust is itself powerful enough to do all that out of the box
Having used Spring has convinced me that framework popularity has no correlation with quality.
A systems language with a high cognitive barrier to entry, compile times and less than a decade of wide adoption can’t reasonably be expected to compete with something like Rails in terms of approachability
I'm switching to Flutter for my UX needs. There's flutter-rust-bridge that binds Rust code concurrently without any headaches in the client, and I can deploy to the web, android, Linux, etc. with ease. Looks p. good out of the box. Got GRPC working quickly, am happy.
Using Rust in the client is nice because I have a single workspace that contains all of my business logic. Backend changes will cause errors in the client code if changes are necessary. The codebase is easy to refactor and trust. Dart ain't half bad.
I stayed away from Flutter at first because it doesn't respect the DOM etc but at this point I'm willing to sell my soul to the devil for how easy it is to make a great UX that deploys anywhere.
I don't love relying on something Google made, though. Feels a little like building a foundation on sand.
Great UX, you say? Great UX!? Flutter’s unashamed pure-canvas approach makes it fundamentally and unfixably awful. Links don’t work because they’re fake (and it’s impossible to fake them), scrolling is atrocious for a significant fraction of users, text handling is obnoxious and wrong… seriously, speaking as a user, I’ve come across things made with Flutter three times in the last four years (plus looking at their demos), and Flutter makes for literally the worst user experience that I have come across in that time.
Now Flutter does actually have a DOM renderer, but it never seems to be used. I’m not entirely sure why. Actually, scratch that, I just went to find a link, and found https://docs.flutter.dev/platform-integration/web/renderers only talking about canvaskit and skwasm, no mention of the html renderer any more… it seems like they deprecated it earlier this year <https://github.com/flutter/flutter/issues/145954>, and will presumably remove it some time soon, doubling down on pure-canvas. Well, at least that lets me more unconditionally condemn Flutter for the web.
Text is bad, especially if you use emoji. Links are bad because they’re fake. Scrolling is bad for a decent fraction of users. And none of these three are fixable if you insist on pure-canvas. (Links you could kinda make work tolerably if you were willing to compromise a little, but the other two just can’t be done.)
If you want more substance to my complaints about the pure-canvas approach, search HN comments for “chrismorgan canvas” or similar. I still haven’t finished reducing it to an article on my website.
If you intend to target the web and care about the web at all, please don’t use Flutter.
There are a couple of options. While not first party, there is this solution for example: https://pub.dev/packages/jaspr
Here’s the difference between jaspr and Flutter Web explained: https://docs.page/schultek/jaspr/jaspr-vs-flutter-web
There’s another package, static_shock, although it’s purpose is to build static websites. The author was part of the Flutter team.
Article: https://blog.flutterbountyhunters.com/you-can-now-generate-s....
In Dart, that is; not Flutter. At that point, you’re probably wasting your time advocating for it, because people are mostly choosing Dart because of Flutter, so if you’re not using Flutter, you might as well use a different language.
There is no one web framework that will satisfy all criteria and all layers of what a web framework needs. Every so often we have to migrate as technology catches up and changes the ecosystem, like wasm did with Blazor.
I’d rather have a language that programming is a joy in. The laziness is a nice side benefit. I can adapt such a language to changing landscape any day. I know Ruby is such a language, and hence Rails adopts it’s joyful mentality. But is Rust a joy to program for? That I can’t say for sure.
Rust would be overkill for most web applications.
It's not that there isn't an answer, it's that there is a problem with the framing of the question itself that should be addressed by the person asking it first.
I also think people underestimate how much impact their slow code has even in networked applications.
Don’t fear, Rust can be as simple and clever as any other language. And the Rust core OSS developer community has spent at least the last ten minor versions improving dev ex considerably.
I think this is the big issue. We as members of the Rust community should be doing more to explain all the patterns for webservers in particular.
Support is there, but it's non-obvious.
Yeah, it’s called loco https://loco.rs/
FWIW I prefer the pile of libraries. Big frameworks are good for scrappy startups trying to push their product out asap, but in most of situations, I’d like a lower abstraction system to build on.
"Do I need zero cost abstractions because I'm writing a computationally expensive very serious project?" If the answer is no use a garbage collected, runtime managed language.
If someone enjoys writing Rust, it's natural that they'll reach for it when they're looking to write something for fun. If the tools/libraries in that language exist to make writing a particular type of thing more fun or easier, then more the better.
Sorry, just spent 6 hours figuring setting up my python environment. Ready to start my hobby project now!
Learning Rust and learning web service programming at the same time would have a steep curve. If you already know the basics of Rust and have written web services before, writing a new one in Rust needn’t be stressful.
1. Do I like the language enough to use it for fun? (no: go to (2))
2. Am I neutral towards the language? (no: go to (3))
3. Am I fine with other people using it for fun? (no: go to (4))
4. Feel free to harangue people who explicitly say they are using it for fun if their reasons for using it is not a 100% utilitarian (which is very likely to be the case: refer to the for fun section)
I think a good part of the devs community has been burned by some framework at some point and doesn't feel the need for web frameworks, hence why nobody bothered to make Rails for rust. In a professional setting, all the services I interact with, have been implemented on top of express (node.js) or fast-api (python). I know people working in ecommerce who rely heavily on django or woocommerce, but rust usage among them is pretty low.
Rust is also one language where there are very little junior people, so you'll see wire-it-yourself library more than frameworks. Eg. sqlx is imho the best compromise in the ORM vs query builder vs plain sql debate: you get typechecked plain sql.
I think axum is very well architected and type checked routes offer a great DX (except for the tower::Service compatibility - sure, it's nice to have compatibility but the resulting code is quite clunky), and I'd rather have smaller components I understand over a huge black box.
I look at Rust for serving web-traffic and I see: dreadful concurrency model (I will never voluntarily go back to async/await after working in Golang), weak client library stories (if I'm writing a service layer for a db, etc.), high barrier to entry, thin overlap with the core Rust value proposition (correctness around memory access, performance).
That's not even addressing the "what happens when my entry-level dev has to write something that interops with a web framework written in Rust." My heart can't take those code reviews, I might as well just write that shit myself without a framework for all the pain that's going to cause.
Note: I don't write a ton of Rust, for reasons that are maybe kind of obvious. I reach for it whenever C seems correct, which is rare but not never (for me).
Rust supports golang style message-passing concurrency if you want it[1]. I'd argue Rust mpsc channels are actually more powerful than Golang's and add richness to message-passing concurrency modeling.
[1]: https://doc.rust-lang.org/book/ch16-02-message-passing.html
Do they support Go select functionality, buffered channels etc without using 3rd party libraries?
Can golang stop you from using a channel when it's consumed/closed?
With the exception of having to write out `async`/`await` the dominant concurrency model in Rust is mostly the same as Go(work stealing CSP). Sure Rust's memory model makes it a bit more cumbersome than Go. Rust's Send/Sync bounds might seem complicated, but those concerns are equally relevant in Go, the compiler just doesn't help you.
I want to make an app in Rust that hosts Servo in it with my business logic in maybe SciterJS - like electron but hopefully lighter?
Something, just a business software guy who wants something with more control than an webapp and not so heavy like electron.
Laziness isn't necessarily a bad thing, depending on how it's implemented.
Any framework is too much extra work to learn and going to eventually get you shot in the foot.
I would say JavaScript is easy, but it’s definitely not the type for a least error-prone app.
If your web app is simple, then frameworks and "safe" languages are unnecessary.
At some stage though, that flips around. There's no way I'd choose to write, say, gmail or Trello in "plain javascript". The learning curve of a suitable framework for projects like that are well worth the eventual productivity, and the number of feet to shoot in your project will way exceed the number of feet added by the framework, and hopefully the framework will prevent way more foot shooting opportunities than it adds.
Jetty, HAProxy, JSTL, commons db connection pooling and Postgres.
It “hasn’t been clear what the templating looks” like because there isn’t any just as AFAIK servlets provide no templating.
Prototype is easy and fast. Scales well to very large user base on a single node. Interactive client side without even writing JS.
I love rust, I really do, I use it for all kind of things. But for web app, the Erlang architecture is so well designed and mature you cannot compete.
Also we use rust and zig from Erlang with native modules for video processing and AI, the elixir/Erlang "skin" gives you immense flexibility and productivity with almost no performance hit.
Now F# on the other hand…
In my opinion you should for 99% of cases use Golang for your web backend. Any other languages there are tradeoffs you are making.
Go:
- very easy to learn and grok Go code
- static typing
- fast compilation
- single binary (easier deployment)
- strong standard library
- large library ecosystem
- go routines for concurrency
- highly performant
Maybe Java,Kotlin and C# but they are still an order of magnitude more complex and resource heavy than Go.
As someone who mainly specializes in .NET, I have had incomparably better time participating in Java and C++ ones because people there are usually able to acknowledge pros and cons of various platforms, how they evolved and where are their strengths and weaknesses.
The average level of understanding and ability to consider what is the dev flow and how the language of choice impacts it in Go ones seems to be just so much worse it's not even funny.
That is to say, goroutines are discount futures/tasks which force you into synchronizing the "yielding of result" manually either via a channel or a waitgroup and a collection, or similar. Not to mention they are also much more expensive than .NET Tasks. I have not measured the cost of Java's new green threads yet but assume they are going to be in the same ballpark of memory cost as Goroutines, but with drastically better steady state performance provided by OpenJDK HotSpot when it comes to regular application code.
And lastly - Go requires you writing heaps of boilerplate for simplest things, Go channels come with a lot of footguns and gotchas you have to learn, standard library has weird omissions, type system is static but weak and as the demands put onto Go continue to become more complex, as more and more developers are forced into it, the language becomes the kind of unreadable soup you accuse other languages of. Just look at range over funcs and iterators discussions recently. It's ugly and token-heavy. And you will see a lot of code like this if you browse random libraries on Github - it's unnecessarily bulky, in a way that is excusable for true system programming languages but not in Go which has even higher level of abstraction runtime than .NET.
Rust can fit in that picture, but it doesn’t need to be routing http.
Most people here appear to agree that types are useful, but some people are claiming that a language that forces you to do memory management is somehow as ergonomic/lazy as others that manage memory for you (with equivalently good type systems - TypeScript, ~Python).
*: There are simpler FP languages that can offer a similar experience, but they come with their drawbacks: I don't think I'd want to set up OCaml on Windows under such a state
Yeah yeah I know <whatever> Framework is great and good for stability or something, but using a framework makes something that I enjoy (programming) feel more like filing out my taxes, just so I can end up with a mediocre CRUD application that more or less works for the project I am working on.
I have removed myself to from the web and programming almost instantly became more fun for me again.
Learn multiple languages.
The whole "I want to do everything in the language I already know" is why the least interesting--and straight-up pretty terrible albeit very capable--languages are dominating our field.
I remember writing a 2.5D "Doom style" renderer thing that let you move around in a bunch of rooms/corridors in real time in MacPerl/Quickdraw back in the Mac OS9 and Perl4 days. Probably taught me more about Perl than I'd learn in several years doing it for a job.
Not everyone wants to be jack of all trades - master of none or have time, energy or capacity to waste their life in hamster wheel of constant relearning of the same thing.
> is why the least interesting--and straight-up pretty terrible albeit very capable--languages are dominating our field.
If they’re dominating our field they can’t be terrible by definition.
^^^^^^^^^^ expected lifetime<Ref<Rc<Arc<&mut str>>>>, found Ref<Cell<&mut<lifetime<mut str>>>>
And development in rust is glacial. It's no one language featurep (not complaining about the borrow checker in particular), but the accumulative overhead of all of them acting in weird ways.
I will say the tooling is absolutely amazing, it's nice to span "app level" and "systems level" concerns in a single language. But it is a lot of language.
Rust already has excellent libraries that are easy to use in many different stacks.
Write one?
I have not seen such a success story for any Rust web framework yet. Although I would love to see one (or more!)
- Regular Web / Services: IMHO, Go, Java, .NET are better candidates there
- IoT devices with Web or API interfaces: Rust is good there. Access to I/O resources etc.
- Extreme high-performance services: Something like a DNS-over-HTTP or wall street data brokers. But for that you need a VERY solid server (but most likely not a framwework).
The regular web case will not produce a framework (out of commercial success), it is much easier to opt for a traditional languages/frameworks and hire inexpensive developers for that. The extreme high-performance services will not produce the framework you want and the likelihood is very small because the number of companies doing that is very low. Leaves the IoT space, which, IMHO, has the best chances BUT will not be the one optimized for your big server machine scenario.
>Routing/handlers
Actix has it.
>Templates
Minijinja or liquid-rust[1]
>Static file serving
actix-files[2]
>Logins
Actix with oidccore is fantastic and easy[3]
>Permissions
Actix FromRequest is literally perfect. We have perm levels (admin, owner) and per-route perms for more fine-grained control.[4]
> Database interface
Diesel with diesel_async for connection pooling has been flawless at scale.
> Admin tooling
We didn't do this, but it would be simple with a bin/load-data.rs file that runs via a docker start command or tmuxp pane.
> Hot reloading
Cargo watch is getting deprecated, but was great. Bacon and and Watchexec are fully qualified successors. CSS watch systems work with the templates already same way as they do for SPAs.
> Background tasks
Make a bin/worker.ts file which defines a single binary and then use redis or another queing sytem to communicate between the worker and core server. We loaded all of HN (40M docs) into a search index with this approach and it was easy.
> Monitoring/observability
There's a decent story here with structured logging. Zero to production in Rust has a good chapter on it (2020) [5]. Lots of progress has been made since and exposing prom metrics endpoint is straightforward [6]. Sentry support is also decent and they sponsor actix.
> Caching
Imo, the story with Redis + actix is fine. We do this with our auth middleware to reduce latency on that operation since it happens with every route and I think it's smooth with FromRequest.
> Emails and other notifications
What's wrong with SMTP? Plus, Keycloak will handle your password reset infra out of the box. SDKs here could be better, but the APIs are fine and not too bad to do with Reqwest yourself.
> Deployment tooling + CSS/JS bundling
Imo, both of these things are the same as with other languages. Rust could use more documentation, but I don't think there's anything making it particularly hard to use.
[1]: https://github.com/devflowinc/hn-search-RAG/blob/main/actix-...
[2]: https://github.com/devflowinc/hn-search-RAG/blob/main/actix-...
[3]: https://github.com/devflowinc/trieve/blob/main/server/src/ha...
[4]: https://github.com/devflowinc/trieve/blob/main/server/src/ha...
[5]: https://www.lpalmieri.com/posts/2020-09-27-zero-to-productio...
[6]: https://romankudryashov.com/blog/2021/11/monitoring-rust-web...
I think this part is perhaps the silliest part of a very silly article. If you really like to put in a minimal effort then why on earth would you use Rust? If you want efficiency, memory management and a compiled modern language just use Go. Then you won’t even need anything but the standard library for what you want to do. Or… use Django as you suggest?
Yes, yes we use Rust in production because we thought it would be easier (well safer) to teach to interpreted language OOP developers than c/c++ but why on earth would you ever use it for the web unless you are a member of the cult?
I think a lot of what people actually like about Rust, to be honest, is that it's essentially an ML language masquerading as a C-like, with a stellar packaging and tooling story. What people fall in love with is the rigorous static typing, the Option monad, the exhaustive enums (which are just sum types in disguise), the traits (type classes in disguise), the borrow checker (a half-way house to immutability) etc. People will brag about Rust's memory safety, and then you find out that they don't really know how lifetimes work, they just .clone() or Arc<Mutex<RefCell>> everything. Which is totally valid, but Rust is hardly necessary for that.
Rust is great, but - heretic idea - not literally everything needs to be in Rust. Go try Ruby on Rails for a while! Or - if Rust has whetted your appetite for more functional styles - Phoenix on Elixir! Yes, it's not quite so blazing fast, but - let's be honest here - you're not going to be getting billions of requests per second on your hobby website where you write about taking apart Amigas. It's OK! She'll be right.
I also want to write code in languages where you don't have to engage in bad software engineering practices to get optimal performance. That usually means aggressive inlining, something that Rust excels at.
edit: the other thing I wanted to mention is that rust's features cause dependencies to be pretty high quality.
If I pass in a slice of something, I'm no longer worried that something ten layers down will change it.
Error handling is explicit and panics are rare, so I'm not worried that dependencies will start throwing exceptions after an update. (This is the reason functions should indicate errors in type signatures—a better ecosystem.)
ADTs mean dependencies represent fewer illegal states internally.
It's just really nice to write code where the bugs are a couple levels up from bullshit like this.
I say this as a Rust enjoyer, but I take the pain because I _really_ want what I'm working on to be fast.
I use Rust often for things that aren't possible in other languages, so I also use it for things that are possible in other languages. (Though I used Python for a production and an art project recently -- with uv it's quite nice.)
I feel like I say this every time this sort of discussion comes up, but I still think that there's a space for a higher-level language with most of what people like from Rust that has a (tracing) garbage collector and is slightly more relaxed with trying to design a way around every marginal heap allocation. Most of the time I bring this up, someone will suggest something like Swift or OCaml, but I think the part people miss is how even despite all of the complexity that comes with being a systems programming language, Rust really goes out of its way to try to be developer friendly despite that.
Yes, it's a meme that Rust programmers are zealous evangelists and want to rewrite the world in it (which is a bit of an exaggeration in terms of lumping all Rust enthusiasts into that group, but there's certainly an element of truth in it), but no one seems to talk about how _weird_ of an idea it is for a language notorious for having a terrible learning curve to be so popular with people perceived to be lacking real-world experience with the domain. How did a language that's supposed to be so hard get popular to the point where people view its fans as pushing it aggressively? You might chalk some of it up to marketing, but I think people undervalue how much effort is put into the little details in Rust to try to make it more approachable, like error messages, standard library documentation, first-class support for all major platforms, and high-quality introductory materials (e.g. both the original and rewritten The Rust Programming Language book, Rust by Example, Rustlings). I don't think the same experience is there if you want to use Swift on Linux (where the support isn't nearly as strong, and a lot of the fancy new things coming out won't be available) or OCaml (from googling right now, a debate on reddit about "which stdlib should I use as a beginner" is on the first page of results when I search "ocaml std" or "ocaml stdlib").
Maybe a language with a similar syntax, traits, monomorphization, and macros would still be interesting to many people? Would people prefer traits and macros over ad-hoc polymorphism (in the style of C++, which could subsume the macro use cases, too)?
Unfortunately people keep placing all GC languages on the same basket.
And before anyone mentions that it is easy to forget, well those languages have their own "Clippy" to take care of it.
They are only second class in the context people put all GC languages on the same basket, and rather go for the X rewritten in Y blog posts.
Because apparently adding newer languages to CV is cooler than mastering the one they have.
I dream about a Rust subset that's exactly that. What would be even better is if you could just use Rust packages directly with it. Since these libraries already do compile, correctness has been verified.
Jai solves this by having a "context", which includes an "allocator" member by default, whose default value is a standard heap allocator. You can override this allocator member to point to your own allocator, so it's easy and straightforward to have the main server procedure allocate a pool of memory, create a new context with a new allocator that uses this pool of memory, then "push" this context for the duration of the request resolution, then free (or mark for reuse) the memory pool afterward.
I gladly take the whole rust "package" because overall it's just so good, at least for opensource / hobby stuff. I wish there was an equivalent but with GC, to use for work.
I think Rust is especially popular with a demographic that was not previously exposed to lower level programming. Meaning younger programmers (because modern languages are higher level) and web centric programmers (because we're in a boom of web development).
This demographic had a hard time entering systems programming, because while fascinating, its exposure is lower (less jobs, less projects), and entry cost (learning C, or C++<11) is harder.
Rust made systems programming more accessible, and systems programming, just as anything related to how things work under the hood, is fascinating.
Now Rust is hard, as you noted, but not hard in the same sense than C is hard.
C is hard because low level stuff bites you immediately. You can't quite easily just use a random library in C if you don't understand your build system, linkage, etc. If you mess up in C, the compiler will not tell you either, you will have to debug your way out of it.
Rust is different, in the sense that its difficulty is limited to the compiler saying "nope". If you can get the compiler to say "yep", then you're 99.9% of the time safe. Now getting that compiler to say "yep" may take time, indeed, but all in all you most often can just get away with sprinkling unwraps, clones and Arcs all over the place until it works.
In that sense, I think Rusts popularity essentially lies in the fact that it is a way to do low level stuff with a barrier of entry limited to being stubborn in learning it.
Yes it does corrupt memory, there are some crashes, I usually bash C, nonetheless it seems we have too much "safety playground" regarding learning processes nowadays.
Popular languages don't really have evangelism or fans pushing it aggressively. Those are traits of smaller languages that don't interop well with other ecosystems so they need a lot of evangelism to build out the library ecosystem.
> there's a space for a higher-level language with most of what people like from Rust that has a (tracing) garbage collector
What non-memory management related things is it people like from Rust that is missing from, say, Java or Kotlin? Because those have great web frameworks that address all the features requested in the article and a whole lot more, there's good first class support for all major platforms, lots of documentation etc. These languages are also famously developer friendly with good error messages.
> What non-memory management related things is it people like from Rust that is missing from, say, Java or Kotlin?
I'd argue that Rust has better interop with C, C++, JavaScript, Python, Ruby, and probably almost every other non-JVM language than Java and Kotlin. I'm not sure why you think that getting people to write more libraries is the goal of evangelization; if anything, I think Rust is somewhat notorious for people writing lots of libraries but comparatively fewer full applications.
Independent of interop (which I'm not really sure is as important to understanding why languages are or aren't popular as you seem to imply it is), I don't think the tooling in Java is nearly as beginner friendly as Rust. It's not just about the code itself; handling dependencies and building an application that you can run outside of an IDE are not nearly as straightforward in Java as plenty of other languages nowadays.
My point isn't that Java is bad or that doing things in it is hard in the absolute sense, but that "it's possible to do this in language X" is not the same as "it would be easy for a beginner to figure out how to do this in language X". I think there's an inherent lossiness in trying to distill what people like in a programming language into a bullet-pointed list of features, and it's an easy trap to compare those lists and conclude that one language doesn't have anything novel to offer based on that rather than the entire experience of working in a language.
You can call into JS, Python, Ruby and other such languages from Java like this:
https://github.com/graalvm/graal-languages-demos/blob/main/g...
It's very easy and requires no build-time bindings generation or Python/JS/Ruby runtimes to be installed. You can add Pip dependencies via the Java build system you use, as if they are regular libraries. It will also JIT compile the different languages together as one so the boundaries are optimized, you get transparent exceptions, callbacks work transparently as the whole heap is GCd as one, you can using a debugger in a unified way across languages and so on.
But this is sort of beside the point. Java once had lots of evangelism, partly to help build out the library ecosystem, but that was done years ago and now there are lots of libraries to meet most needs. So as a consequence you don't hear about it as much. This thread is a case in point. Lots of people suggesting rarely used languages like O'Caml or Zig, nearly nobody suggesting more obvious candidates that are used for this task, every day by nearly every big company in the world.
> I'm not sure why you think that getting people to write more libraries is the goal of evangelization; if anything, I think Rust is somewhat notorious for people writing lots of libraries but comparatively fewer full applications.
Wouldn't that be expected then? Rust has had lots of evangelism which has successfully yielded lots of libraries?
> handling dependencies and building an application that you can run outside of an IDE are not nearly as straightforward in Java as plenty of other languages nowadays.
I think this may be based on an outdated idea of how things work nowadays. I have my beefs with Java build tools but if you just want to build and distribute a web app it's easy. Using the stack I'm most familiar with:
1. Starting from a blank computer, install IntelliJ or other IDE of your choice.
2. Go to https://micronaut.io/launch and use the little wizard to pick whatever languages and features you want to start with.
3. Download the generated project, open it in your IDE. All the dependencies are now downloaded automatically including the build system and the Java runtime itself. Likewise if you picked features that use JavaScript.
4. Tweak it, run it. To ship it you have several options, but an easy approach is to add this to your build.gradle file (if you're using Gradle):
dockerBuild {
images = ["[REPO_URL]/[NAMESPACE]/my-image:$project.version"]
}
and then invoke the dockerPush build target, either from the CLI (./gradlew dockerPush) or the IDE. You can also compile it to a standalone Linux executable with another build target. That's all there is to it. I don't think Rust improves on this situation. Note that the above instructions work on any computer in the same way, including Windows, with no additional work required.I think people want to write programs that run on an OS rather than an interpreter.
Not that installing Java is all that hard. apt-get install openjdk is sufficient.
Besides, GraalVM can produce a native executable for pretty much any JVM language/program.
And if free beer is your thing, GraalVM native image or OpenJ9 also produce a regular executable.
That's kind of the whole point I was trying to make above; if one language makes something super easy to do without having to look for instructions compared, that makes a difference in terms of how people will decide whether to learn it. Individually, lots of small quality of life things add up and can make a language that otherwise would be unapproachable way easier to get started with than languages that don't prioritize that sort of thing.
Scala is a great example where I've seen the "best" option being rewrapped libs with scala calls and types rather than a native solution and the dev XP just isn't as good overall.
The dev xp, I sorta agree on, but it has improved a lot.
I am all in for not wasting more and more resources.
It really can be small stuff too, like hiding that nested generic "GC adjacent" type salad to be accessible only if you need it, via a type alias. Yes, I can define that myself, but the point is that a lot of people need it often, given its widespread use.l, so why not provide one?
I'm sure there's reasons not to do the above example, but that's not the point. It feels like Rust is at 95% of being amazing, and that the remaining 5% is attainable if we want to.
At this point, I think a new language is more likely to provide this niche than Rust, and I also don't think that has to be a bad thing. Having Rust scoped more to lower-level programming where you're more worried about things like minimizing heap usage and avoiding copying strings or whatever rather than trying to be all things to all users might end up with a better experience in the long run.
This is unnecessary gatekeeping. It also shows your lack of perspective. Or, rather, your lack of imagining other perspectives, probably.
Sure, rust is primarily a language aimed at systems programming. But it also is so much more (and also a cult).
* Its type system is excellent. Especially the lack of a "null". Even if, like me, you're fine with a GC, the type system alone is worth dealing with this insistent borrow-checker.
* Its multithreading is stellar. The borrow checker helps a lot in making it such.
* Its mutable/immutable model is highly practical. It's what makes the threading stellar and the type system more useful than in any other language I worked with.
* Its "oop" model is uncomfortable at first (coming from Ruby and Java) but forcing the "composition over inheritance" by not having inheritance is probably the best thing that can happen to "OOP". Every solution where I used inheritance, I shouldn't have.
* Its culture of "making the good way the easy way", being opinionated, and a community that adheres to this, is worth dealing with a thousand borrow-checker WTFs. cargo fmt, clippy, etc.
* Its build system that generates binaries that can just be "plopped" onto a server, "chmod +x" -ed, and ran is unprecedented.
I come from Ruby, Java, Javascript (typescript). I do a lot of Python, maintain some go services, have decades of PHP under the belt and occasionally dabble in some C and even C++. I can find my way around in a C# codebase and Objective-C. All have their strengths and weaknesses. Their place and use-cases.
But rust, for me, is the only "ecosystem" that ticks all checkboxes in nearly all situations. It has downsides, and I consider the borrow-checker to be one of them in a lot of my use-cases, but it's something I'm willing to deal with gladly, because rust's other benefits.
the argument was that in early dev and prototyping phases you don't want to write good, clean, correct code but move fast and break things while not caring about edge cases - and this, the author argued - is relatively hard in rust.
making the good way the _relatively_ easy way
But I do like that even in this stage, one is forced to at least make the decision to have terrible things, explicit. Like with 'unwrap()' and 'expect()' littering the code in PoCs and exploratory projects.
For me, a bigger problem in this stage is that I lack the information to make decisions on types. I have this same problem in TS and Java. It's guaranteed that I'll shape types in ways that will prove difficult, or slow me down a lot next week. I'll be spending time refactoring types when I should move on to the next feature.
I guess, but that's just my experience, that statically typed languages are just not well suited for early stage software. When we don't know the shape of the data we'll be pushing around, nor know anything about the shape of the layers, ports, modules and so on. Which is why I'll grab ruby for these kind of quick explorations often. And once the shapes emerge, rewrite it in rust :)
Arguably a louder “plop” due to size, but then you don’t have to chmod.
A lot (all?) of the points I make aren't unique to rust, or even invented in rust. Many have even better implementations in other languages. Go also invented the "fmt", with one opinionated code style "enforced" by default - bikeshedding be gone!
My point was that it's the combination of all of these points. AFAIK, go ticks many of these boxes too. But for me golang falls short with mutability, and with the type system (though that one's catching up really fast). It's the "package-deal" that I like about rust.
But wasn't Go a lot more limited in the amount of targets it can build for than rust?
Way to lose readers with memeish statements like that. That said it's in good company of cultists like Smalltalk and Lisp community ;)
> * Its "oop" model is uncomfortable at first (coming from Ruby and Java)
It's OOP in the sense it has polymorphism, and "methods" . It's not OOP in almost every other conceivable way.
It doesn't fit with static OOP of Java. It doesn't fit with dynamic OOP of Smalltalk and Ruby.
> It has downsides, and I consider the borrow-checker to be one of them
I wouldn't want Rust without borrow checker, and never figured this complaint.
Yes, it's uncomfortable and will prevent you from making some legal code.
Guess what? So will a seatbelt. You move too fast to reach something and it snaps you in place.
I am not convinced that the mental overhead justifies the memory safety guarantees yet. At least for a general purpose language.
I didn't yet write a lot of Rust, perhaps more experience trivializes Rusts ownership model.
Wait. What? Neither Java nor Rust limit class/struct per file. You must mean public class per file.
As a fellow Java dev, no it doesn't look like Java at all. Maybe it looks a bit like Kotlin, but only superficially.
I wrote Rust on and off for 5 years, by that time you internalize the borrow checker.
I didn't mean syntax or core lib to be similar, I just generally meant that both languages impose restrictions on themselves which might seem sensible at first.
That said, a good way to think about programs is a series of restrictions, i.e. invariants. Truth be told, only Ada Spark so far really embraced invariants.
And thus, maybe if you're writing something that doesn't need to be screamingly fast absolutely all the time there's a register of Rust where putting lots of things in Rc<Box<T>> is completely acceptable, in much the same way that you might use a lot of impolite words around your friends but you don't in front of your employer.
Show HN: How to program in Rust as if it was old school C++ with pointers
This is not remotely a trade off I’m interested in making.
Kotlin is serious about null-safety.
> I think a lot of what people actually like about Rust, to be honest, is that it's essentially an ML language masquerading as a C-like, with a stellar packaging and tooling story.
F# seems like a good option.There is also Visual Studio and Rider.
That’s a dealbreaker in many situations.
F# can usually handle C# things - they put a lot of work into ensuring interop with new C# features - but the languages are from different paradigms so it is sometimes a bit awkward despite F#'s comprehensive OO support.
Personally I struggle a bit with F# because it doesn't have typeclasses, and a language that looks that much like Haskell but doesn't have typeclasses just feels weird to me.
Although it might get them... C# are looking at adding a traits-type feature (they don't seem to know what to call it yet, but the design's been kicking around the language team for ages now and keeps getting discussed), so F# could presumably piggyback on that if they wanted.
Why not name it Traits?
I'm in a startup using .NET. We deploy to a variety of targets including AWS t4g (Arm64) instances in AWS as well as x86/64 targets in GCP. All devs are on M1 Macs. Our build pipeline is GitHub Actions Linux runners. Our DB is either AWS RDS Postgres or GCP Cloud SQL Postgres with a mix of EF Core as ORM and Dapper (for more complex read queries).
C# has, over the years, converged with TypeScript so they are very similar[0] (though no Duck typing in C#). Good mix of OOP and FP paradigms borrowed from F#. F# interoperates with C# so it can tap into the larger C# ecosystem.
It's a good platform; very productive. CLI has a functional hot reload (much more limited than Node on JS as the granularity of module replacement isn't quite as good).
Don’t know if you intended this joke, but some time before Rust 1.0, @ was the sigil for garbage collection types (@T, like &T these days). Which I think was just reference counting, without even a cycle collector, because was shown to be undesirable before it could be improved.
I'd recommend Haskell as an alternative, but Rust "fixes" plenty of long term Haskell annoyances (especially around laziness by default, concurrency, unsafe std).
Being able to use Arc<Mutex<T>>, Rc<RefCell<T>> and other smart pointers gives you granularity and control over what happens to your memory. I think it's a nice feature to have to mentally reason about your memory usage (incidentally a weak point in Haskell); even if it were on-par with a GC implementation-wise, why use a GC over this?
I like to have control over my code (and I'm probably one of the few who think monad transformers in haskell were a good feature - despite their clunkiness) and I'd pick explicit code over "magic garbage collection in the background" any day. This is not to say I like verbosity for verbosity's sake (eg. think React Redux in JS vs the implementation of the same idea in Elm) but in some cases I think it's justified and it brings extra value.
Who changes their minds about what to do for fun because someone on HN thought it was unnecessary? Doing what some random person wants instead because they want to be “heretical” (because it’s a religion right) sounds like the opposite of fun to me.
But I’ll see you on the next thread about making a Rube Goldberg web server in C++ templates. Or some such very necessary and “approved for prod by HN” thing.
As someone running a fairly cpu heavy SAAS application for users who literally dog pile our whatsapp support forums if it goes down for even a second, Phoenix is pretty damn fast in production. I won't claim its as fast as rust but it will run circles around ruby or python and for IO bound tasks, competes perfectly fine with go. Its never been a bottleneck (database is another story)
The benefits are less obvious if you have less of those constraints: go is way way simpler to write large codebases for cloud applications in and still provide an excellent concurrency support. Python is way better for experimenting.
I guess that if all you do is hammer nails then a multitool is just a hammer.
You gave a long list of benefits of Rust - which should have answered your own question - and then suggested people use an ecosystem with almost none of those benefits.
I’m sure it made sense in your head.
That basically leaves you with Rust's type system. Rust's type system is pretty great, and if we pretend we can't hear the Haskell developers it's one of the best type systems out there. That might seem to get in the way of quick prototyping, but on the other hand it would mesh really well with a framework like Django. One of the great things of Django was that you define your data schema, and Django takes care of both the database and a passable admin area. I'm sure you could greatly expand on that principle, with data types driving a lot of behavior and conveniences that the framework just handles for you.
Maybe a bit like .NET, but without the enterprisy coat of paint and without putting dependency injection everywhere.
All of them with better ergonomics for Web development.
Last time I looked into OCaml, I struggled to find a way to interact with the database in a type-safe way, I never figured out which standard library I was meant to install, I was being encouraged to use 2-3 different project management tools (Dune + Esy + OPAM), and I gave up on writing tests. But at least there's a garbage collector!
I realise these are all problems that I'll wrap my head around over time, and eventually they'll seem completely trivial to me, but the introductory documentation on getting started as a professional (and not as a first-year student doing a French-language compsci degree, which is what most of the documentation assumes), is pretty dire.
Meanwhile, much as I'm sure I overuse `.clone()` and `[A]Rc<_>`, the ownership model of Rust is deeply useful. It's something I often find myself missing in Javascript - not necessarily because I want to produce the most performant code, but because it's useful to understand the lifetimes of the objects I'm keeping around. Am I accidentally storing a reference to something in a closure that I forgot about? When this object gets deleted, have I checked that it's the last possible reference to this object? Etc.
Like, I don't think everyone needs to learn Rust. It's a great language, but there's lots of other great languages out there, depending on your personal and business contexts. But I think this idea that Rust can and should only be a low-level language feels absurd to me. It is a fairly ergonomic language with a fantastic ecosystem, a powerful type system, and an ownership model that will be useful even if you do try and opt partly out of it with GC-like wrapper types.
Yes they do, unless you get your editor to magically type `.clone()` and `[A]Rc<_>` all over the place.
Not to mention they don't need unsafe, or 3rd party crates to handle graphs.
Additionally, all of them have interpreters and REPLs alongside their compilers, streamlining the code-develop-debug loop.
In fairness, I think a lot of this comes down to familiarity. I'm fairly familiar with `.clone()` and Arcs at this point, so they don't really change much in terms of ergonomics. Usually their usage is fairly obvious, and quite often my editor literally does magically type the `.clone()` calls through LSP fix commands. It's the same as, say, OCaml's insistence that recursion is better for complex loops than, say, `while` — it's not necessarily wrong, but if you're unfamiliar with the idea, it's going to feel weird.
More important to me is the ergonomics of the broader ecosystem, and that's something that Rust has done well, that other languages just don't seem to be interested in at all. Things like integrating testing into the standard workflow; working hard on getting the stdlib in good condition; having an excellent ecosystem of well-documented, usable libraries; or designing error messages and lints with a focus on getting people to understand how the language works and not just what they've done wrong this time. I've really missed that stuff whenever I've tried MLs. You mention, for example, REPLs, but a unit test is basically a saved REPL session that you can repeat every time you make a change.
I'm not trying to convince anyone that Rust is the best language in the world or something. I really like it, but I find it helpful to think in terms of object ownership even in non-Rust languages, and I can understand why for other people that sort of approach is unhelpful. But I would love to see other languages embrace its ergonomics more, or new languages created with that as their focus.
But... that's what I do in a unit test anyway. A unit test is essentially a REPL session that I've frozen in time, can replay whenever I want, can debug, can trigger a fresh run whenever changes occur (which isn't quite hot reloading, but often a darn sight more useful), and can keep track of and share with my team. Which means I'm not just able to explore things on my own, but I can see the explorations that other people have made with their code.
As for unit tests being more usefull, depends on how much one is willing to wait for them to run in Rust, given its compile times.
Albeit it makes the process easier compared to a really shittly written code without strict typing, but against good codebases, it takes the same amount of time.
If you think about correctness of a program, (i.e for any combination of given input , it changes a state in a determinstic way, including no state change for invalid input).
Strict typing is one way to accomplish this. The cpu does not give a shit about types. It cares about memory registers and locations. The unique code built into the compliers/transpilers is the thing that validates the correctness of the program in this case.
You can move that code into the testing suite without relying on the complier, and just do testing. Generally, given competent programming skills, this takes about the same amount of time as designing a well structured program - your tests are pretty much your design document for the thing itself.
I think you're arguing that tests and types can both be used to check a particular case for correctness. Sure, this is true. However "moving type checking code into the test suite" means nothing—that would just be type checking.
When you make a code change, there's a difference in the kinds of feedback you get from your tests and your types. Tests usually cover business logic or stories—things that you want or don't want. Types ideally cover everything. They check every operation applied to a given piece of data. Of course types are rarely precise enough that they can catch every logic bug (e.g. strings with semantics not encoded in types, like email addresses).
This is just scratching the surface. You might just have to try out both to get a better sense of how they feel to work with.
We basically ended up creating a tool+language spec that would let us define the mapping of input to output sequences. We wrote it in such a way where we sat with scientists and pretty much mapped all the possible cases that they could think of for valid inputs and what the code should produce.
Then this tool would basically write automated tests for us, in such a way as to not only test correct behavior, but also do combinations of inputs out of order, fuzz testing, and so on. We ended up making it also check memory state, to ensure that there was no memory leaks, and analyze the memory space for required data or data that should not be there.
In the end, whenever someone was developing anything for this software, they would basically just run the tests, and it would be very good at catching possible errors, mostly on the negative side (i.e for a fuzzed input, it would result in an output that should be an error).
We could have done the same thing with a typed language, but it would have to be very strict typing to the point of something like CoQ, and it would have taken us probably the same amount of time to write that.
[1] yeah, there are type systems like lean, coq that can do both, but the proving process is just currently not realistic for everyday applications
in my experience it doesn't
1. refactoring are in my experience still much faster (and reliable doable) with rust compared to e.g. Js/Python even in presence of a lot of tests. This is a bit less of an point with Java/TS/C# etc. through I had some very bad (and also some good) experiences with refactoring in TS. And to be clear I don't just mean pure refactoring but any larger changes affecting many places in code which might be needed to idk. impl. some feature.
2. especially with Js,Py and similar there are way to many edge cases you can have to test for all of them. Sure most times this mainly matters when writing libraries but on larger projects does apply too. Stupid stuff like you expecting a `list[int]` and someone (externally i.e. outside of your tests) passes a `dict[int, int]` and that happens to work as you current impl. is only linearly iterating it as if it's a `Iterable[int]` but then you change the impl to require a `Sequence[int]` as you access it by index in some corner case and now you customer has really strange subtle bugs. Can't be caught by your tests as the problem is the customer but still breaks the customer which is always bad and can't happen with rust. Sure also won't happen if everyone uses type annotations and mypy correctly and strictly. Through you can't rely on your customer using mypy, but you can rely on your customer running compiler checks (as it's the only way to build the code). Also while mypy is much much much better then pylance it is still prone to issues as both `cast` and `ignore[..]` are things you sometimes need but which easily can hide bugs if the code changes (cast doesn't pin the "from" type and ignore is scoped by place not by what is wrong)
Except you can’t test for correctness. Tests can’t prove the absence of bugs.
This all goes out the window when people throw “unwrap” all over the place because “this should always succeed”.
Rust has exceptions, they are just named Result and people just as frequently decide not to handle error results as not catch exceptions in my experience.
Typed exceptions are just as good as what rust offers IMO.
TypeScript has an equally powerful type system.
Python's type system is almost as good.
Both have are significantly more productive when it comes to writing software, and have bigger ecosystems and lower learning curves.
> It makes a lot of progress towards the goal of "make invalid states unrepresentable", which could be really useful for web apps.
TypeScript and Elm are both good at this while being useful for web development today.
A simple example of the (many) things you can't do in TS:
struct StateStart;
struct StateEnd;
impl StateStart {
fn foo(self) -> StateEnd {
StateEnd
}
}
fn main() {
let start = StateStart;
let end = start.foo();
let another_end = start.foo(); // this won't compile
}
Try doing that without a runtime crash in TS. Super useful pattern for state machines (which are the building block of computing).I miss more features of Rust in TS that features of TS in Rust. All the time.
But TS has so many dark magical patterns that I'm still hoping to be proven wrong.
How does TypeScript's type system prevent shared mutable state?
It doesn't.
And does Rust have type unions and intersections, interfaces, and mapped and conditional types?
Shared mutable state is a problem even in single threaded code (for example, modifying a collection you're iterating over)
> And does Rust have type unions and intersections, interfaces, and mapped and conditional types?
You can typically accomplish the same thing with enums/proc macros/traits of course—with the additional benefit that the type system is designed to be sound. Soundness is an explicit non-goal of TypeScript[1], so once you start layering on those kinds of overly-clever types, you soon reach a point where you're just lulling yourself into a false sense of security.
[1]: https://github.com/Microsoft/TypeScript/wiki/TypeScript-Desi...
...which is an extremely rare source of bugs. The vast majority of errors caused by shared mutable state are related to concurrency.
> usually
So, not always. That is - TypeScript's type system contains features that Rust's does not have.
If you ever put an await anywhere in your code, an arbitrary amount of random stuff might run between the time you await and the time the awaiting finishes.
Same applies to older mechanisms like callbacks and promises.
Race conditions are more rare there because as long as you aren't doing any IO, there is indeed no concurrency, so you can't e.g. get two threads trying to increment a single variable at the same time. They can still happen, though, especially if you accidentally do a partial change to an object, put it in an illegal state, and do IO before you finish that change.
Confidently asserted, but very debatable.
> The vast majority of errors caused by shared mutable state are related to concurrency.
Concurrency, like async-await, you mean?
> TypeScript's type system contains features that Rust's does not have
Sure, and likewise Rust's type system contains features TypeScript's does not have—for example, just try expressing anything close to traits with TS's type-erased generics. Since the feature sets aren't the same, I suppose it's a matter of opinion which type system is preferable. But I know which one is more helpful for me for sure (especially considering the aforementioned soundness issues).
Simply so untrue I’m not going to elaborate further
idk. many years ago through stuff like service workers, having shared memory through TypedArrays and similar.
But for the discussion more relevant is that shared mutability constraints do not only matter with threading, they matter with any form of concurrency (like JS async/await/promises and before that callbacks). And even without that you still have other single threaded and single tasked concurrency with the classic being changing a collection while iterating over it. So even without classical forms multi threaded concurrency still very helpful.
> And does Rust have type unions and intersections, interfaces, and mapped and conditional types?
Rusts type system is mainly nominal typed while TS is mainly structural types so it's a bit hard to compare. Like if you nitpick then you could maybe argue that based on TS type system having "features rust types system doesn't have" it's more powerful (but you also could argue the other way around it depends on how you count them). But that would be misleading as it ignores that not only are they two fundamentally different approaches to typing it also ignores that some features are implemented through other means outside of the type system.
Practically having used both I can say that while TS has some things in the typing which are a bit cumbersome to do in rust when it comes to helping me having correct code in context of changes, especially larger changes, and especially libraries Rust still yields better results in my experience. Naturally assuming you don't abuse the TS in either case.
(and to technically answer the question, type unions == yes but different, intersection == no but also make little sense in rust, interfaces == yes but different, mapped types ~= depends on the aspect in some yes and better then TS in other no but implicitly through derives so worse then TS, conditional type ~= again handled through different features depending on usage either associated types or feature gates)
For everything else, there’s Go IMO.
Sometimes you do have to rethink what you marshal vs what you just manage manually, but that’s what pointers are for (that can be turned into ref Ts for single values and into Span<T>s for slices/arrays/etc). The idea is that performance ceiling of FFI in .NET is as cheap as direct calls.
For me, a clear advantage of rust (and also Go) over Python (or Ruby, or PHP) is not having to deal with runtimes. With Python, the server needs all sorts of setup. Exactly the right version and configuration of the runtime. Pip. Or Pipenv. Or Pyenv. Or Conda. Or... and so on. Then it needs the libraries installed. Some of which must be built natively; so just packaging them in some CI is often not even possible. These native builds need lib-something-dev packages. Then with an update of the underlying server, suddenly nothing works anymore.
Having a single binary that I can plop onto almost every linux box and which "just runs" regardless of the exact version of Ubuntu, cargo, rust-toolchain on the server/hosting machine is invaluable. I'm so done with maintaining the fragile card-house that my Ruby-on-rails apps require, or that my python/flask services need, that my nodejs insists upon: with rust this is no longer needed.
And yes, docker/containerization is a solution to it. And yes, I know about the options to package up a python app with runtime included. But all that adds extra complexity. It merely makes it easier sometimes. Not simpler. And it inevitably makes troubleshooting, upgrading, etc even harder in some future.
Eh, Rust's type system isn't one of the best out there. It's lacking higher kinded types, etc. It's abilities in type level programming are frustratingly limited as well.
So it's advanced aside from from Haskell, OCaml, Idris, Scala, etc. Compare to OCaml's effect system for an advanced type system feature.
And with that in mind, I do agree with the GP. Scala's type system, for example, is full of warts (well, Scala 2; I still haven't tried Scala 3). Rust's is cleaner and has fewer gotchas. I very rarely have to look up how to express something in Rust's type system, but I remember when I last did Scala, I often ran into weird type-system related errors that I just didn't understand and had to dig into to figure out. Some of that, sure, is likely due to Scala's type system having more features, but some of that is because it's just more complex. And I would usually rate something of lower complexity as better than something with higher complexity.
Lots of other small annoyances like the whole tuple situation have also been fixed.
EDIT: Plus: intersection types, sane macros/inlining, etc.
Unfortunately (for me), some of our projects still have to cross-compile to 2.x, but that's irrelevant for greenfield. I'd give it a whirl -- it's a great improvement over Scala 2.x.
> See, if I want to make something for the web, I could use Django but I don't want that. I mean, Django is for building serious businesses, not for building silly non-commercial things!
Personally, I spent the first decade of my career switching between languages, believing that I should use the best tool for any given job. I spent the last ~five years doing mostly Rust, and I've learned that there's a level of mastery of a language you can never quite reach if you're always context-switching between them, especially a language as “deep” as Rust. This means I write things in Rust that Rust is not the best language for.
In the past I’d try to justify why I’d done something a certain way when another way would’ve been faster / better / cheaper, but I now realise that (at least for personal pursuits) an acceptable answer to “why?” is simply “because I wanted to”.
If I were putting together a web development team, would I recommend Rust? ...probably not. But that's because I'm putting together a team, not a playground. I'm paying people. I want to use common, well-supported, time-tested methods, unless steering away from that is truly needed to make the project successful. For web dev, that assuredly ain't Rust ("yet," some may add).
But for me? Just for me? I think it's a language I'll always enjoy. Within that line of thinking, I do feel there's room for better web dev tooling in Rust, though what's already there is probably enough to at least get started.
Let's just say different people have different opinions on this one. Personally, I find Go to be a big regression in language design given modern PLT research, and wouldn't use it unless forced (e.g. terraform providers). Other people have other opinions: a friend I respect a lot has great success in the Go community.
I'm telling you that because if, as a Go enjoyer, you're baffled by the writings of a Rust enjoyer, it's very likely the result of different perspectives rather than silliness. And if your conclusion is "just use go", you're probably the one being silly.
The person likes writing in Rust, and would like to have a batteries-included web framework so they can tie together a web app quickly on the language they like to use... And I know they did not include https://rocket.rs/ in their 'existing ecosystem' section.
What you're asking for is a DSL built on top of Rust for the purpose of creating web apps.
It'd be cool to have an out of the box "90% of what a website needs" toolkit that means writing your 10% custom code/secret-sauce in a modern code security obsessed language. Even if it is opinionated and "batteries included" in ways that make some things annoying.
I have built many projects that start with a WordPress install, to piggyback it's CMS, user management, and admin backend. I then inevitably end up making a choice between writing the project in php, or jumping through hoops to use the WP features (like auth and user accounts) in some other language that's more appropriate to the task at hand.
> What you're asking for is a DSL built on top of Rust for the purpose of creating web apps.
I don't think that's necessary, personally, but if someone really wanted that, I'm sure they could come up with something that can be wrapped in a Rust macro invocation, that gets expanded to something smaller and faster (compiled-code-wise) than anything you can get in a Rails app.
In other words, I find it faster to produce working stuff in Rust and much faster to produce quality stuff.
Obviously I’m just one data point.
An explanation for "why bother try to have a low effort web server in Rust?" that I haven't seen mentioned: Once you're past the introductory phase of being familiar with Rust, if you don't need to pedantically handle every error case, it can be _very_ simple.
A low effort "doesn't have to be very flexible" web server would be terrific & easy to use in Rust.
Having said that, I'm not aware of the actual offerings available. Maybe there is one, other posts have suggestions.
That said, what exactly was the issue you had with Go?
(Not only because I think competition is good, but because I really do not enjoy writing Go.)
Go look at job boards. The competition for Go, Rust, Django or whatever is Java+Spring. That's pretty much the industry standard unless you're in a Microsoft-only shop.
Edit: I read other comments. Disregard my replies then, because this is likely intentional. Luckily, I had enough interactions with Java communities to know that many other people there are not biased.
I don't see how people can "love" writing rust either. I love a lot of things about Rust and its ecosystem, but the syntax day-to-day isn't one of those things.
Saying that and then use Rust is like saying, "I want hassle-free driving, that's why I drive a car with a 7-speed manual shift".
If you want a full-auto, then use it. "Minimal effort" is the reason why people wrote garbage collectors in the first place.
My problem wasn't so much dealing with memory management, but trying to put an upper bound on memory usage and failing nicely when that upper bound is met. I generally don't like working with garbage-collected languages, or having to use some niche application-specific language when I can just use a general-purpose language instead.
I've had a lot of fun playing with it, it was very nice & simple to use. It's also foundational in the Rust ecosystem, so it's well maintained.
“I like to play basketball but tbh I like to put in the minimal effort”
“Then why do you even play basketball? It’s a physical activity…”
The first thing takes precedence over the other.
> If you want efficiency, memory management and a compiled modern language just use Go. Then you won’t even need anything but the standard library for what you want to do. Or… use Django as you suggest?
Just do what someone else dictactes. The surefire way to self-expression and Fun Town.
> Yes, yes we use Rust in production because we thought it would be easier (well safer) to teach to interpreted language OOP developers than c/c++ but why on earth would you ever use it for the web unless you are a member of the cult?
You forgot to capitalize. The Cult. Since you apparently want to look silly.
There’s a difference between what you do in production (at a job presumably) compared to what someone does to be “silly”.
Rust is great, high level language that's super convenient for implementing arbitrary algorithms on arbitrary huge, complex data structures safely and quickly. Value semantics is absolutely unique and useful. The system of traits and standard library using traits like Ord or Hash means that it's enough for your type to implement one of those and suddenly it can be a key in a HashMap or BTreeMap and can be a part of even larger and more complex data structure. Adding a web to that easily could be super useful.
Seems like a good enough reason to use it to me, but perhaps I’m just another cult member.
> we use Rust in production because we thought it would be easier (well safer) to teach to interpreted language OOP developers than c/c++
I think rust is just safer period, regardless of your level of expertise or background. Or are you saying that every memory safety bug was written in by someone inexperienced?
Not to mention the concept of “sharing code.” See I may write garbage tier rust code that basically glues together a bunch of libraries, many of those libraries are written by programmers way better than me, and I can gain performance and safety from using their work. The performance difference between languages like Rust and JS is huge, even if I, tainted by my background in Java, write dogshit code.
I think you're reading too much into that and creating conflict where there isn't any. GP just meant that they had a bunch of interpreted language OOP developers (probably Java or C# or something like that), and they wanted them to start writing code in an AOT-compiled language (yes, I know about Graal native image; not the point). And that teaching them Rust is probably going to result in safer code than if they were to teach them C or C++.
That shouldn't be a controversial statement.
JIT vs. AOT is not the same as being an interpreted language. For most applications, running on top of the virtual machine is a good thing, I don't see JVM developers turning to different languages just to escape the JVM, outside of some niche projects.
I was trying to out that wanting to take the easiest path was rather contradictory to working with Rust.
> I think rust is just safer period, regardless of your level of expertise or background.
Yes, I’m not sure how you think I was implying something different. We (specifically) adopted it because it was easier for our (specifically) developers who weren’t familiar with low level languages to work with compared to c/c++.
> many of those libraries are written by programmers way better than me
You shouldn’t sell yourself so short in my opinion.
If Rust had a good full-fledged web framework, it would enable more developers to justify why the business should use Rust. The culprit is that it would require a heavy education budget, but it would in turn enable the business to allow using Rust for other parts of the business which could benefit from Rust, e.g. middleware, small components, CLIs and systems programming in general.
Having a little bit of Python here, a little bit of Go there and a bit of Java elsewhere can become chaotic. There is a huge benefit for a small company to only have 1 programming language everyone agrees on using.
If your goal is easy web development, Django, Ruby on Rails and Laravel are frankly going be extremely hard to beat. I prefer Go with templates, but that’s still much worse than those options.
I really like the Rust tooling and I like exhaustive pattern matching. Python post-dev debugging time is probably 10x vs Rust. That's why I choose Rust.
Because you like the Rust ecosystem?
Because you enjoy developing in Rust?
Because it's a fun language and you really do get to avoid the borrow checker nowadays if you just use `Arc` and `Clone` all over the place. I'm the same as OP.
Also, I love the package ecosystem. I know it (rightfully) gets some of the flack that the JS ecosystem gets, but most of the time, I truly don't care. I enjoy C programming as well, but C not having a common packaging system / cargo equivalent has really hurt it a bit in my opinion.
I'm an incredibly lazy developer and Rust only makes me lazier. I can pretty much turn off the part of my brain that deals with "programming language" stuff and put all that energy towards the part of my brain that deals with "building stuff" whenever I write code in Rust, because I have a high level of confidence that the language itself isn't trying to make me shoot myself in the foot at every turn.
Rust is the only language where I can open something like Notepad without an LSP or highlighting, write code for an hour without testing or compiling, and then run clippy a few times to see and make the suggested fixes. It doesn't get any lazier than that.
On the web framework topic: Rocket is the greatest "lazy developer" web framework I've ever used.
You can't be that lazy, or you would use a language that doesn't force you to do memory management.
> Rust is the only language where I can open something like Notepad without an LSP or highlighting
This is a very weird and arbitrary qualifier that seems designed to filter out other languages with type systems.
This is not my experience at all. I never think about memory management when I'm writing Rust, let alone am I "doing it" (active voice).
Your second comment is a very weird sentence that is very difficult for me to grok.
Worrying about lifetimes is memory management that is unnecessary with GC languages and directly increases cognitive load.
My second paragraph is pointing out that "where I can open something like Notepad without an LSP or highlighting" is a strange qualifier. Why does it matter if you don't have semantic analysis in your editor? You certainly didn't explain it.
Again, not my experience at all. I simply don't think about any of that stuff when I write Rust, all my mental energy can safely go towards working on whatever I'm building.
If you don't believe me, you're welcome to watch the literally hundreds of hours of me live coding in Rust on YouTube.
Your subjective perception is not reality. You have to think about it, or your programs will be incorrect. The mental load may be low to an experience Rust programmer, but it is there, and it's very intrusive to Rust beginners like myself.
> If you don't believe me, you're welcome to watch the literally hundreds of hours of me live coding in Rust on YouTube.
That's not relevant. Lack of external indicators of cognitive load does not mean that it's not happening.
A self-whoosh? I'd consider myself a Rust beginner (e.g. I've completed Rustlings, I have written a handful data parse and transform programs on my own and a couple of web servers to benchmark stuff, nothing grandiose) and lifetimes haven't ever gotten in my way, they usually get automatically inferred or the compiler tells you what is wrong and how to fix it. Most code examples and docs I've worked with, like Rust By Example, Rust Cookbook, even Rocket's docs, don't even feature code with a lifetime annotation or they don't really need you as a developer to do anything with them other than copy and paste if necessary. This is the smallest hill to die on.
I am pleasantly surprised to see however that this unfortunate subthread produced interesting and well-intentioned comments from others.
> Assume good faith.
https://news.ycombinator.com/newsguidelines.html
You have no idea what's going on it my head, yet you choose to believe that you do as a result of me countering your arguments. This is a great example of why the Rust community has a bad reputation.
> Watch the literally hundreds of hours of me live coding in Rust on YouTube
Your definition of "lazy" seems quite different from the standard definition.
In my view, the average "incredibly lazy developer" of 2024 tries to have an LLM write most code for them (probably in JS/TS/Python), doesn't notice the subtle bugs introduced by said LLM, and ships straight to prod. If using an LLM fails, they go back to StackOverflow to look for an answer that they can copy-paste. When that fails they give up and ask a less lazy developer for help. At no point in this process did they read the documentation for the language/framework/etc they are using to accomplish their goals.
As soon as you start writing large enough programs to start to learn/worry about variable scoping you are kind of "worrying about lifetimes though. If you know that using global variable is often a bad idea, then you are, in some sense, worrying about lifetimes.
I first learned this in the mid/late 90s as my Perl4 cgi scripts started to grow beyond about a screens worth of lines, and it really solidified into an almost unconscious background task as I wrote code once I started writing a lot of Perl5 apache mod_perl stuff a few years later.
These days, I find myself experiencing slight cognitive dissonance writing tiny Arduino sketches with a bunch of global vars declared right up the top.
Yes, to a much smaller extent. Rust's object lifetimes, for all the performance gains you get, add on a layer of additional cognitive and development overhead that is simply not present in memory-managed languages.
Lifetimes are not about performance gains. They're about correct behavior under concurrency, which you have even on JS with async-await.
Heck, let me correct that: even under programs without concurrency it's very easy introduce subtle bugs due to shared mutable state (something as simple as having a global mutable variable, you don't even need concurrency to mess that up, concurrency just introduces more issues like race conditions).
> cognitive and development overhead that is simply not present in memory-managed languages
No, the cognitive overhead is there, you're just ignoring it (and thus more likely to introduce subtle bugs).
If anything, Rust lowers the cognitive overhead by taking care of that for me statically at borrow check time.
Lifetimes are just types (literally). If you think lifetimes are cognitive overhead, so are regular types. Same argument could be used there since types are "cognitive and development overhead that is simply not present in dynamically typed languages". But I wouldn't write anything beyond a few thousand lines of code without them.
When I write Rust I spend more time thinking about types than about lifetimes.
...or small enough programs!
I fought a lot with the borrow checker when I started learning Rust, and then I learned to embrace it. The borrow checker only occasionally bugs me!
As I write more embedded Rust and Rust for WebAssembly, removing implicit dynamic memory allocation forces me to think a lot about lifetimes again.
That's why I just... don't. I use `clone` liberally. That's it.
In Java, you can just move something around in any way or shape, change its lifetime semantics very liberally (e.g. make it have a single instance, or make every thread have its own, or make it have its own instance of X as a field, or just use a shared one), and it will compile without a word and the runtime will handle it.
In rust, in my experience (though it is surely significantly less than Yours), these kind of refactors actually require code changes at almost every location, and might recursively cause further such issues. I think there is a sort of bias here as well, that people kind of like this kind of refactor where the tooling readily shows you the exact error and it may not be too difficult to solve each case, so this might not register as annoying, but quite obviously a stricter lifetime system will have to have some downsides as well.
Using notepad for code is just masochistism though
Eh, depending on what you're building you don't do much if any memory management in Rust. You just declare what you want and it's automatically released at the end of scope.
Yes I know the borrow checker. With experience it stops getting in the way while catching the occasional bug.
And no, building linked lists or recursive data structures are edge cases that you don't need to develop for most applications.
Last week I deployed a Ruby change where a missed comma between two strings in an array did not get picked up by any of the automated tooling or by 3 other team members who code reviewed my change, and ended up cutting 50+ sev2s at 10pm.
AWS folks can probably guess which internal tool I had to touch that requires Ruby.
(I was on call, fun times)
I resonate with you even with TypeScript, since it's just annotating types but not the true value under the variable. You would have to go to a greater extent to make sure that everything from outside (library code, parsing HTTP responses, database queries, etc.) conforms to your annotated types. Even in the same project code created by other people, I often doubt the validity of the types, asking myself questions like: Should I validate this? What if this has already been validated, thus making mine a performance waste? Does this object contain extra sensitive information that could be leaked when used in a logger?
After having experience with Rust, working with TS feels like navigating a minefield where every refactor could detonate hidden bugs or behavior collectively accumulated by all the historic code and dependencies, small or large.
> anxiety when I'm forced to touch fragile bits written in Ruby
Wild, because the first sentiment is exactly how I feel about Ruby.
Uhh have you tried Cursor compose?
Wait, what, you think we need another web framework?
.....buuuut, if you spend more than 5 second actually reading the article...
> This doesn't exist right now, and I'm not sure if anyone else is working on it. All paths seem to lead me toward "whoops I guess I'm building a web framework." I hope someone else builds one, too, so we can have multiple options.
...
> My toolkit is called nicole's web toolkit, or newt. It's available in a public repository, but it's really not usable (the latest changes aren't even pushed yet). It's not even usable for me yet—this isn't a launch post, more shipping my design doc
ie. -> https://git.sr.ht/~ntietz/newt/tree
Is where you can find the code, for what they're trying to make, to fill this gap.
Sure. Not a web framework. An all in one opinionated good out of the box defaults like rails.
Yes, there's no django/rails for rust.
Or go. Or clojure.
In fact, a lot of people have tried to make all-in-one frameworks, but usually the way the wind blows is that doing something specific is easy, but doing something flexible and generic is hard, a lot of work, and unless you're being paid to do it, it's a loveless job that eventually you get sick of and abandon.
I feel like if you're really going to go and write a rails clone, you need to spend a little bit of time digging into some of the other failed attempts and reasons they failed (eg. 1, 2, 3) first.
The landscape is littered with aborted rails clones.
It's perhaps... fun, but ultimately futile and naive, to think you can be 'the one that makes it' when so many others have failed, if you don't take the time to understand why so very very very VERY many attempts to do this in many languages has failed.
(NB. Not to pick on clojure; I could have picked a bunch of go examples instead, but I guess the clojure failures have been particularly visible to me since I use clojure, and it's a place where, like rust, currently there are a lot of 'bits and pieces you can assemble yourself' and it feels like, surely just slapping them all together in one package can't be that hard...)
[1] - "Clojure needs a Rails" https://news.ycombinator.com/item?id=32288291 [2] - "A Rails like framework for Clojure." https://news.ycombinator.com/item?id=822210 [3] - "Luminus – A Clojure Web Framework" https://news.ycombinator.com/item?id=22852375
The idea of creating a "one man framework" like Rails in a systems programming language like Rust is a very tricky one. I don't think you can mimic the best productivity features of Rails without metaprogramming. Yes, you can do something like it, but the abstractions would leak all over the place making it uncomfortable for a Rust developer and not good enough for a Rails developer.
When she says lazy, she just means that she'd like a web framework that takes care of the most common and obvious schleps that are needed to create a web framework, like routing and so on. Once you get those schleps out of the way, there's still tons of work that needs to be done. Nothing lazy about that.
How do you convince someone to use an alternative? The knowledge needed to understand why will help you get close enough in Next.js anyway.
2. NodeJS. Not everyone likes it.
3. It is slow. Yes it is! Try to get good web metrics with Next I dare you!
4. Premature release of App Router. Will they do something like that again.
In our experience elevating the web metrics in Next.js takes the same expertise as doing it in any other framework. Our experience with Vertx and Microsoft’s dotnet web frameworks have been good, but Next.js got us to a fast, featureful, performant website sooner and with more flexibility around requirements. I won’t pass judgement on rust frameworks I haven’t used, but it’s just to say that in an honest accounting, a naked backend framework is less than half the product when you’re talking about web applications actually worth making with original ideas. Unless you work at Google.
But for SEO, shouldn't Google be given a 100% pre-rendered, essentially static page? Next.Js plus our CloudFront seems to just support this.
CDN is for content. Useless for say a Google search type problem. So I call an API endpoint for data in my component. But what if I get rendered 600 times? Well luckily I have memoized the call! As long as it was under 2Mb that is.
I don't see myself doing it any other way these days.