I spent about the past month learning rust - and decided it just wasn't a usable language for me.
I think what it comes down to is that I'm just not that into bondage and discipline from a compiler. Yes, I know, it's trying to make my code 'safe', and I'm horribly cavalier and I should feel bad, but:
1. the borrow checker rejects valid programs, has a lot of corner cases it can't catch, and is in active development
2. people routinely write more inefficient code to satisfy it
3. people routinely freak out about 'unsafe', which is a bit much given the first point
Anyway, YMMV. Maybe you're smarter than me, or rust fits your mindset better, or you enjoy asking for help on discord all day, but I am so glad not be using it anymore.
Just curious, what was your programming background. Have you used C++ a lot especially “modern C++”?
I find that as a C++ programmer, Rust encodes in the compiler a lot of best practices about ownership and memory management. Using Rust is a breath of fresh air in not having to worry about dangling references.
From experience programming C++, I can see what the error messages are trying to prevent and why it is important.
However, if your experience was primarily with managed/garbage collected languages, a lot of the Rust compiler pedantry is stuff that you never had to worry about to being with and you have never experienced the pain it is trying to prevent.
I had a stint as a C++14 programmer, and I switched over to C++20 for this project.
You covered the downsides pretty well, big upsides for C++:
- ability to use C libraries directly - and thus original C API docs. I've got battlescars from NPMs deep dependency trees, and cargo is a similar story.
- I don't need the (very patient and knowledgeable!) people on rust discord to hold my hand every day. There's a stackoverflow for almost every C++ issue.
- that 5% edge case where the codes safe but the borrow checker isn't happy I can just write. No RefCell or nightly compiler.
Of course you will also write the 5% edge case where you've wrongly convinced yourself that it's fine. In general the sort of self-discipline that would likely ensure this doesn't happen also satisfies the borrow checker which might be why some people find this not a problem and others really struggle. Worth somebody studying perhaps.
I think that's a big philosophical difference I have with the rust culture - static analysis is a tool, not a religion. Useful, but nowhere near enough by itself to make reliable software.
> ability to use C libraries directly - and thus original C API docs. I've got battlescars from NPMs deep dependency trees, and cargo is a similar story.
This is something Rust is relatively good at, you just may need some unsafe blocks, but assuming you can compile using clang for the C code everything tends to work well. I am saying this as a Rust skeptic.
Agree with this wholeheartedly.. I was a hardcore c++ dev by day for 7 years straight and picking up rust was like the best thing I ever did. If anything it made me a much better c++ programmer by the end.
> 3. people routinely freak out about 'unsafe', which is a bit much given the first point [borrow checker]
It sounds like you're making a common misconception here. unsafe code and the borrow checker are not closely related, and unsafe blocks are not a way to play fast and loose with ownership issues. See https://steveklabnik.com/writing/you-can-t-turn-off-the-borr...
If you don't want to deal with the borrow checker, the solution isn't unsafe, it's Arc, Clone, RefCell, etc.
This is a red flag for your complains: Is VERY likely you believe Rust is wrong, when the fact the problem is that your code is not good.
I know this, my first 3 months starting Rust were thinking between "I'm a failure as developer" and "Rust is wrong" or "What? Why can't do this?". For first time after learning more than 12 languages I was truly in shock. And even, for second time in 20 years, picking a book and PAYING attention to it.
THE major problem with Rust is not, not even close, the borrow checker or that is picky. Is that Rust idioms are not OO idioms, not C/C++ idioms, not PHP idioms, no Java idioms.
Is fairly a simple language, actually, and the productivity is very high (today I code in Rust faster than python, that is still my benchmark on productivity).
What is NOT simple, is that it require several things to "click" and align properly to make the journey smooth.
And that it reveals how much other langs hide for your.
P.D: The problem you say, sometimes are true, but only at first, when the people are not fluent on the lang, or later, when doing seriously advanced stuff.
Not think I bashing you. This experience is common for many in the community, and even very strong developer get the weird experience with Rust. But also is true that when Rust "click" is very, very good!
P.D.2: I bet Rust is much harder the more experience a developer is and the more ingrained some ways are. I teach Rust to some newbies, and the experience for them is far smother.
> This is a red flag for your complains: Is VERY likely you believe Rust is wrong, when the fact the problem is that your code is not good.
I'm gonna be blunt: This is the worst thing to come out of the Rust community, bar none. Rust is a fine language and I love to write in it, but I loathe the way Rust developers have convinced themselves that Rust is an axiomatically 'better' language and that the things you can't do in Rust, or the things that are painful to do in Rust, are a perfect circle venn-diagram overlap with the things that are just bad to do in general. It's a mindset that shuts down discussion and constantly shifts goalposts. It doesn't help anyone.
Who knows, maybe rust clicking for me was just around the corner. I kept thinking "finally I'm understand this" and then something else would happen.
I'll leave it to the incredibly patient programmers who can spend 3 months just learning it. I'll use all the time I save to write more tests, and probably end up with something much more correct.
After all, you are human, and we humans aren't good at exhaustive covering of all bases. Sometimes when I am reading a rust compiler error message about erroneous borrowing I really wonder that I never could have thought about that corner case.
It's true. Sometimes Rust compiler's pedantry is just not neccessary. Then write Python, TypeScript, Ruby or in Rust box, arc and clone everything.
> I'll leave it to the incredibly patient programmers who can spend 3 months just learning it.
I understand, I was close to drop it.
In retrospect, I create a lot of self-inflicting problems in my journey (like I learn Rust making a programming language and hit by coincidence some of the hard stuff with Rust!) and knowing what I know now, Is clear the time running in circles was in big part that.
But, of course, is hard to see you are running in circles when you are running in circles :(
The instances of safe code that the borrow checker can't verify as such are not "corner cases" by and large, they're genuinely non-trivial patterns, often with non-obvious drawbacks such as lack of composability/modularity. Rust devs and researchers are working on better abstractions to support those cases, but these are very much the exception, not the rule.
> people routinely write more inefficient code to satisfy it
People should stop freaking out about this. The "more inefficient code" you might get by adding a few .clone() or Rc<RefCell<…>> when the borrow checker complains about something is still way more efficient than anything written in alternative "safe" languages.
The "slow" solutions are a bit noisy but that's a good thing, since it tells you where it might be worthwhile to either refactor for efficiency or document why a refactor isn't feasible.
> The instances of safe code that the borrow checker can't verify as such are not "corner cases" by and large, they're genuinely non-trivial patterns, often with non-obvious drawbacks such as lack of composability/modularity.
Unfortunately, the borrow checker can't handle a basic observer without falling back to patterns that violate encapsulation. The observer pattern is one of the best patterns for composability/modularity, and I find that the borrow-checker-compliant solutions are more complex and don't solve anything but rather move problems somewhere else.
It's cases like these that make me appreciate Rust's wisdom of adding Rc<RefCell<T>>.
>People should stop freaking out about this. The "more inefficient code" you might get by adding a few .clone() or Rc<RefCell<…>> when the borrow checker complains about something is still way more efficient than anything written in alternative "safe" languages.
But you don't have to use a "safe language". C++ can avoid a lot of copies and be just as safe from an absolute point of view. The borrow checker isn't god and Rust isn't perfect. In an absolute sense the C++ code you can write could be more correct and efficient(it just doesn't tend to be).
You can always just go down the UnsafeCell/pointers way and will still be safer than C++ as the borrow checker still works for inside unsafe, and that the few places where a memory error can lurk will be highlighted by unsafe blocks.
That's Godel's incompleteness theorem for you. Every static type checker also rejects valid programs, but that doesn't make them any less useful. Just one of those inconvenient facts about our universe.
Yup, this is the tradeoff you have to make to catch the wide variety of bugs. For personal use, probably not worthwhile, but it definitely pays off for the safety it provides in production contexts.
The way I view Rust's philosophy is "it's better to catch bugs at the compile stage than at the execution stage".
Not really? Python's dynamic typing and lack of variable declarations clearly shows it was designed for developer velocity, not catching bugs.
Seems like your argument is Python catches some bugs when converting to bytecode, so it cares about catching bugs early. That's not true -- for the most part, Python catches only the most trivial of typos before execution.
I agree with this. My experience was that the learning curve for Rust has a huge kink in it right where the borrow checker is.
Type checkers provide tools that make it comparatively easier to ignore when they could be wrong or are getting in the way. To do something in a type-safe manner often doesn’t require re-abstracting behavior.
This content, while still accurate (Rust is Web-ready, and all the frameworks listed are excellent!), is missing out a few credible and recent options.
In particular I want to shout out for Axum (https://github.com/tokio-rs/axum), from the folks who brought us Tokio. I've been building with it for the past few weeks and have enjoyed every minute.
I started with Warp, but ended up struggling a bit with middleware. I tried Axum as a web framework wrapper for tower (https://crates.io/crates/tower), and never ended up bothering to reintegrate back into Warp.
Warp's filters (middleware) were the same reason I left for Axum. Axum is very similar to Warp ergonomically, without the heavy macros and with sane middleware. I think Warp paved the way for Axum in many ways though, so it deserves a lot of credit.
I have been toying around with Axum a bit and it is nice as a web framework. I really appreciate the many examples in the repo.
Having come from python (day job) I did hit a screeching halt when it came time to meaningfully interact with a database.
It is unfair to compare Rusts current offerings to something as mature as SQLAlchecmy but boy does it slow things down dev wise to be mapping my own types and queries using tokio-postgres.
I've looked at Diesel, sqlx and SeaORM but have not yet committed to learning one yet.
I've also used Warp for some small web server stuff in the past as well. It is an interesting approach but definitely a little different.
Although you do get the initial screeching halt, I’ve found having those types mapped out and with sqlx actually makes for a very nice and fast dev experience eventually. Having typed queries right in the IDE is pretty amazing.
Similar to typescript, where years ago I thought “but I just want to write code!”, now I can’t imagine going back. At least not for meaningful projects where I need to care about consistency and stability for more than a few months/for something critical.
The initial slowdown affords you a lot in the longer term.
I am picking up SQLAlchemy as a long time Django ORM user.
I’m somewhat wide-eyed at how much simpler and more readable query building is in Django. I have been spoiled.
SQLAlchemy is closer to SQL than I generally want to be for pedestrian web programming. It also lacks conveniences I’ve taken for granted writing Django backend.
Even for writing data migrations in scripting, having ORM queries be incredibly simple to read and modify is extremely valuable, it makes code reviews accessible to more people.
I’m not a rust developer, though I admire what it can do. That said, having looked at the starter queries on each, it would take a lot to make me contemplate use of any of the rust-based ORMs you’ve listed.
As a sibling comment points out, typing and async are valuable and Django has no typing and is still working on delivering async support in its ORM.
That said, for web products reducing time to flipping feature flags seems to be the main goal.
In that way Python / Django and I suppose even SQLAlchemy will probably retain users against Rust.
You might be interested in our community interview with Tokio Axum maintainer David Pedersen. It is recent and covers a lot of intermediate Axum stuff. https://www.youtube.com/watch?v=nZLimYT4EHs
I've spent the last few months porting the guts of a 100 KLOC PHP command-line utility I wrote to Rust. Thanks to the wonderful Rust documentation it's been a mostly painless endeavour.
- a bunch of time thinking about the borrow checker
Quick iteration and concise readable code are much more important in web-land than in system programming land, and those are key reasons why I don't think many will be abandoning interpreted langauges for Rust.
I sometimes wonder whether people adopt Rust/Haskell/ReasonML for front-end work because they want it to feel like more of an effort than it otherwise would.
You may want to check out Crystal https://crystal-lang.org/ -- you will get all of the positives you describe, while avoiding perhaps 3 out of 4 of your listed negatives. (I think your "ease of iteration" point will still probably win for PHP vs any compiled language.)
Also, I'm very curious: how does the KLOC count compare for the PHP and Rust versions?
> I think your "ease of iteration" point will still probably win for PHP vs any compiled language.
While I don't think you can beat straight php/js/python for this, there's still a lot of room for improvement over the rust/c++ magnitude of compile times.
For example Go/OCaml/Common Lisp (at least, SBCL is a compiler, there are lisp interpreters), all have reasonably quick compile times. And the latter two have repls, which really does help improve the iteration cycle.
I haven't had to play with crystal, where does it fall on this spectrum?
> how does the KLOC count compare for the PHP and Rust versions
I don't have exact numbers since it's not a straight port — the original PHP code was written to analyse a slightly different language than the Rust version I'm building.
I'd estimate that Rust requires you to be about 10% more verbose than PHP. Main annoyances vs PHP: no default argument values, and only a single let allowed inside an if conditional.
My company has its code basically split in half between Python and Rust. Our Python code uses mypy pretty aggressively. The iteration speed difference is... drastic. Rust far outshines Python in terms of our ability to get something done, maintain it, and iterate on it over time.
Did you mean to write that python beats rust? I find it hard to believe Rust is faster to 'get something done' than Python. Happy to be proven wrong, of course.
That is something I would find hard to believe, if you mean iteration of changes. I have coordinated a few rust rewrites, and at least seeing engineers live programming, it takes far far longer to change and compile Rust code than python.
Could you talk more about your company's process or how you mean?
The time it takes to run `cargo check` is nothing compared to dealing with bugs, terrible tooling, weak errors, etc. All of those areas have been radically better with Rust.
As an example, type errors in Rust are radically better than mypy, it's no competition. In general rust's type system is just way better, we have to use Any all over the place because mypy can't even handle a Json type (no recursive types at all).
There's nothing that Python really does better in terms of iteration that I can think of. Obviously you don't have to compile code, but `cargo check` is seconds, that is not the bottleneck. We use hypothesis in Python and quickcheck in Rust, cargo is way better than pip, working with loosely typed data is about the same in Rust, maybe a bit better, and working with strictly typed data is wayyyy better.
The tooling for working with crates, having an actual lockfile, higher quality libraries, etc. It just all makes Rust more productive.
We've run into maybe one rust footgun, which is that binding to _ drops the value immediately. We've run into so many Python footguns, like, did you know that in Python if you want to do circular imports you need to put your imports at the bottom of the file? Or use absolute imports everywhere? Neither of which seem to work 100% of the time by the way, something that makes writing graph data structures quite cumbersome. The other day we ran into a stray comma turning a value into a tuple. The papercuts and footguns across the tooling and language just add up really fast and basically don't exist in Rust.
I don't know how people get stuck on the borrow checker, frankly. It's one thing if you're very new to the language, but within weeks it should be very simple to avoid those errors. I maybe hit a "fuck, what" borrow checker issue once a year, in which case I hit up the Discord, they fix it, and I file an issue with rustc so that the error message will get improved.
This is based on about 7 years of Rust and Python experience, with 6 years of professional experience in Python and 3 in Rust.
I'd like to hear more about the Rust rewrites you've coordinated. Why do you think Rust took longer to change than Python?
I've felt this a little bit, but can only theorize why; the borrow checker makes mutability into a viral leaky abstraction, such that a change over here can sometimes cause widespread refactors on other components that would be decoupled in other languages.
Jury's still out on whether that's healthy long-term for a codebase, especially once the original writers have left. Jury's also still out on whether it encourages us into better architectures to begin with. Maybe it evens out, maybe not!
> Why do you think Rust took longer to change than Python?
Potentially the issues we ran into were due to the inexperience of the team, Rust despite being ~12 years old, most of the programmers we were working with had less than 2 years of experience with it. So if you have a more experienced team, your mileage may vary.
Given that, I think it has to do with the typing system, it is a lot faster to change your structs in python and propagate the changes as needed than in Rust. Asking for another field to be displayed on a web dashboard could take ~1 hour for a team we had working in Django, and ~2 days for a team working in Rust. Of course the projects in question were wildly different and a lot of other caveats, but still in general we found that Python was just much much faster to make changes with. I was not directly involved in any of the programming, but did review the changes, so my perspective may not be the correct one.
That's great for you and your team, but looking at https://github.com/grapl-security/grapl it seems like your needs are pretty different from most web developers.
It's more a CRUD app that has many features added in the first six months, and fewer features added thereafter, and then a few months go by and a new developer takes over and has to figure out how to add yet another feature.
That model favours easy-to-google frameworks with wide ecosystems.
I imagine that especially for a command line utility you also gained an easier deployment story when using rust, given the static linking and lack of interpreter. Requiring PHP to be installed to use a tool has caused me issues, especially with some of the recent PHP 8 updates introducing breaking changes. It’s a better story for PHP on a server where it’s more centrally managed.
The deployment story of the PHP tool in question is pretty good thanks to the semi-official Composer package management system (it hasn't been officially endorsed, but everybody uses it).
People just run `composer install --dev vimeo/psalm` and it works. It can also run on any web server that runs PHP, so I have it running on psalm.dev
OfC the Rust equivalent is very easy to package, and also runs reliably in the browser via WASM (currently ~1.5MB compressed).
> I sometimes wonder whether people adopt Rust/Haskell/ReasonML for front-end work because they want it to feel like more of an effort than it otherwise would.
That's ridiculous. There's a lot of garbage to unpack in that sentence, but for example Rust's borrow checker provides valuable guarantees about your code and eliminates entire classes of bugs. It's not just wasted time. Haskell's purity and lazy semantics allows for equational reasoning and more composable abstractions. ReasonML makes it really hard to accidentally write a bug that crashes the program. Etc.
Just because you don't see why those things are valuable doesn't mean they aren't.
Rust code is significantly more readable. Use the type system to your advantage.
> - - really smart type inference
Rust has really smart type inference too and only rarely ask for an explicit type. I've found that I prefer annotating my types. It adds to documentation and readability.
> - a bunch of time thinking about the borrow checker
You can use Arc/Mutex to overcome some of these and review them in a refactoring.
Rust has smart type checking. The really smart stuff is (IMO) the beauty of the borrow checker.
In PHP, modern typecheckers can verify that the first element always exists on an array after an emptiness check:
if ($some_arr) {
echo reset($arr);
}
Whereas in Rust you have to explicitly unwrap:
if !some_vec.empty() {
print!("{}", some_vec.first().unwrap());
// .. more code
}
The Rust version requires you to use your intuition to figure out that `unwrap()` will never panic here.
In my Rust port I consistently rely on the PHP typechecker's knowledge of the equivalent code when using `unwrap()`, because it knows which array fetches are safe.
> At the cost of introducing a bug/technical debt.
At the cost of shipping a product!
-------
Edit: user ibraheemdev proposes this Rust equivalent which more closely matches the idealised pseudocode, and type-checks:
if let [first, ..] = &some_vec {
print!("{}", first);
// .. more code
}
It's preferable IMO to
if let Some(first) = some_vec.first() {
because the latter relies on a property of a non-empty vec, and checks that property, rather than checking the non-emptiness of the collection.
> The Rust version requires you to use your intuition to figure out that `unwrap()` will never panic here.
You should not use `unwrap` in a production product. It's there for prototyping and I think it's a mistake they have it (though the language already requires a good upfront time investment as it is). Use `?` and have your error propagate accordingly to the top of the chain. Have your own error types and convert from other error types.
if !some_vec.empty() {
print!("{}", some_vec.first().unwrap());
// .. more code
}
I don't know why you would check the first element of a vector. Use Rust type system to your advantage, and don't "convert" PHP code into Rust. Re-design your program. Also you don't need to check that the Vector is empty if you are using "first". "first" returns an Option with None if your vector is empty. My guess is that you have yet to exhaust the idioms Rust offers and approaching it in a PHP-like manner.
> > At the cost of introducing a bug/technical debt.
We tried this style in one of Google Earth's packages for a while (using an internal C++ class similar to Rust's Result). So many operations could theoretically fail (e.g. indexing out of bounds or an expected key not existing) that pretty much every single function in that package returned a Result. These "unexpected" errors would just fly up the stack, manually unwinding it, because nobody could really do anything with them. Once we had that, I really questioned some of our policies.
I can imagine a language where panics don't crash the entire application, but instead blast away the surrounding "region" of memory, leaving the rest of the program intact and able to keep running. Similar to catch_unwind, but if the language is aware of region boundaries, and only allows certain methods of communication between them, it would leave the rest of the memory in much more predictable possible states.
It may sound surprising, but it's actually possible with the right language constructs (Erlang folks know what I'm talking about!) and that way, we could once again have panics for unexpected errors, and Results for expected errors.
> don't "convert" PHP code into Rust. Re-design your program.
Trust me, I am!
For example, I'm rewriting all the (very-efficient) PHP regex code to a much more convoluted version because Regex::new is so costly.
Less sarcastically, I am embracing the Rust Way where necessary. My complaint is that the Rust Way sometimes forces me to depart from the idealised pseudocode version of a given algorithm much more drastically than PHP does.
> Yes, but IMO that makes the code a bit more abstract:
> You've left behind an explicit "is this collection non-empty" and you're instead relying on a property of a non-empty collection.
More abstract for who? I think virtually all Rust programmers would easily understand the `if let` snippet, virtually all PHP programmers would understand the PHP snippet, and virtually all programmers of any language would understand that a non-empty array has a first element. I'm not at all convinced that most programmers would correctly guess that a function called `reset` is used to access the first element in an array though.
A collection is non-empty if and only if it has a first element. Therefore checking for non-emptiness is the same as checking for the existence of a first element.
I think that's obviously a matter of opinion. I don't think Rust is hard to read at all, nor do I think of it as being particularly sigil heavy, but that's me. I think the syntax is probably unfamiliar for some, and familiarity is almost always what people mean when they say "readability".
Coming from c++, Rust was pretty familiar. PHP isn't that hard to read either.
Most Rust programmers just never, ever have to deal with pointers like this. And most code in the wild looks nothing like this. Yes, it's there if you need it, but in practice you will never read anything like it.
Readability is the most suspect of all the anti-Rust arguments to me, because, as others have noted, it's almost always about familiarity. The most senior parent comment is someone explaining how their 100KLOC PHP command line program(!) was more concise and readable than Rust! PHP as the exemplar of readability. Think about that.
Look -- if I really loved PHP and was forced to write Rust, I might really hate it. But I think the world of people who really love PHP is pretty small compared to the world of people who have lived with PHP because that was what was familiar. Folks let's climb off the bus to crazy town.
Depends. Rust is quite readable for a low-level language that does expose every little bit of detail of memory-management. But I do agree that we should not over-promise its readibility.
A bit of one, a bit of the other. It's fairly well-optimised. I think the big remaining optimisation will be to de-allocate memory less frequently — the program creates and destroys millions of structs representing types. 20% of the runtime is taken up iterating over and dropping collections of type-related structs.
Yes — that's after a ton of Rust-centric optimisation. About 25% of the runtime is currently consumed with deallocating memory (there are a lot of heavily-nested data structures getting cleaned up), so there's definitely some more work to be done to reduce cloning.
When a person converts their 100 KLOC PHP command line utility(!) to Rust and that's not a bigger red flag than their negatives list, it's a world gone mad.
> When a person converts their 100 KLOC PHP command line utility(!) to Rust and that's not a bigger red flag than their negatives list, it's a world gone mad.
I'm not converting, I'm just porting a good chunk of it for a slightly different purpose.
Not sure what's a red flag -- the original PHP utility has 17 million downloads and people are pretty happy with it.
It was a joke. It's great that people like your utility, and that you're trying Rust.
The source of my amusement was -- I really, really don't see the world the same way as you do.
I won't take your negatives point by point, but, for instance, familiarity and readability do really seem to be in the eye of the beholder, because, to my eyes, to call PHP concise and readable, especially as compared to Rust, is a spit take. PHP is emphatically not readable to me. Writing a 100KLOC project in PHP? Ugh. Yeah, you do you, but not this guy.
When it comes to JSON serializers, there is a Java framework that is faster than Rust, which is interesting to say the least. When you start comparing actual fully-features frameworks, many Rust libraries even lose their advantage!
There are definite benefits to Rust web frameworks (despite the minimal speed diff, Rocket or similar can have less than half the RAM requirements that other languages run into!) but just because Rust compiles to machine code doesn't mean it'll revolutionize your web application.
> These provide everything you’d expect from a web framework, from routing and middleware, to templating, and JSON/form handling.
One problem I've run into is that I wanted to give Rust a try for a basic CRUD application but the features just weren't built in or available in packages. Some frameworks were missing CSRF, others were missing multipart/form uploads, and so on. Online discussions often end up concluding that "it's easy to write the middleware yourself, see this hacky POC", but this type of code is easy to mess up and easy to break with API upgrades.
Tide and Warp seem to have matured quite well, so I guess I'll have to give them a go at some point, but I'm a little sceptical. The status of this issue (https://github.com/http-rs/tide/issues/852) is a little disheartening...
Not that I think those metrics are all that relevant, but can someone explain to me how just-js ranks so highly? Very much defying my expectations in comparison with some of the top representatives of compiled languages.
I so deeply wish for a set of benchmarks that accepted only idiomatic, standard code that a slightly above average developer would plausibly write.
Pretty much every set of benchmarks out there is ruined by monstrous unrealistic code that squeezes out 5% more performance than actually well written code.
As a side note, one flaw of TechEmpower benchmarks is that to my knowledge they use the original "wrk" tool, which only supports HTTP 1.0 and is sensitive to "coordinated omission" [0]. This means it ends up being biased in favor of web frameworks that implement specific optimizations or have specific behaviors that aren't actually useful in a production context.
Not sure, but it’s pretty funny how the most popular frameworks (rails, Django, flask, laravel, etc) are way at the bottom with 3% the score of the winner.
I honestly don't know. It could be a consequence of the way the test is set up, or how the total score is calculated, or maybe the code was optimized better than other frameworks were.
The SQL code seems quite optimized (manually compiling database packets instead of using a library). Most code seems quite static and very small, so it's probably very quick. I don't think you can actually use the framework like that in practice, though.
"Rust has mature and production ready frameworks in Actix Web and Rocket"
Why is Rocket considered production ready when it's only just at version 0.5? Similar <1.0 stories can be found on crates for sqlx, rusqlite, and postgres. Am I wrong in assuming the API must be quite stable before it can receive the 'production ready' label?
I feel like they should probably advertise this fact somewhere, as I couldn't find it in their docs, website, or readme, and it is quite a significant detail.
Here's an example of how useful webassembly can be: we're at the (fairly early) stages of developing PRQL [1]. Because it's in rust, with WASM we could put a live compiler on a webpage [2], and recompile the PRQL into SQL on every keystroke.
There's no server process — it's all built in GitHub Actions, hosted in GitHub pages, and runs in the browser. The whole WASM code is 164 lines (o/w half are comments).
This website really needs an update. Many people mentionned Axum. On the DB side you should check out sea-query and sea-orm.
Many negative rust comments over here. It sure did take me a few months to get comfortable with the language and its idioms, but man do I not want to go back to languages without a borrow checker now. The compiler and clippy is just so good at catching bugs. Give it a few years to mature the async/await (it is a mess currently no gonna lie) and it could displace Golang especially for cases where you care about tail latency.
Someone hands you a waffle head hammer seems like a more apt comparison.
If you can get the hang of it, it’s a safer hammer. You’re still going to be manually managing your memory allocations though. The nailgun people have garbage collection (and yes for the gc folks, I’m aware you can still write allocation free code, or at least take manual control of allocations in a garbage collected language and therefore opt out of garbage collection overhead - but why would you unless there was a performance issue)
I don't manually manage my memory anymore in Rust than I do in garbage collected languages (say Python or Go). (I do have to think about object lifetimes and whats pointing where in all three mentioned languages).
As an aside, I really like not having to manually free locks, close files, release connections back to pools, etc. It's one resource management paradigm that applies to everything - not just memory.
Don’t get me wrong, I really like rust, but you can’t deny that in Java or python you can just put/share this class/object here that changes the lifecycle of a bunch of other components and it will Just WorkTM thanks to the GC. While in Rust such a refactor will be safe in the end, but you will have to manually look into a bunch of uses of said struct(s) and recursively refactor the whole thing while fighting (or being helped) by the compiler.
This is the (in my opinion) insurmountable difference between low and high-level languages. Even though rust is (arguably) probably the best/most readable low-level language that as you note can sometime even beat managed languages (locking/file close), it will loose to major refactor-speed.
>You’re still going to be manually managing your memory allocations though.
I'm writing a 10,000 LoC library and I call drop() manually only 3 times, in exactly one place, and not for memory reasons. I'm not sure what you mean. If you mean thinking about lifetimes, sure. If you mean thinking about drop(), almost never.
I’d guess that not many people are using rust for work compared to how many use it for fun. If that’s true then most people using rust are doing so because they enjoy it, not because they’re forced to. So most people with an active interest in the language are the ones who enjoy it. It’s similar for other niche languages which haven’t made it into many workplaces. Compare that to the mainstream languages which a lot of us have to use whether we like them or not, and so we have opinions about them which are not necessarily positive.
Because C is 50 years old and boring. And Rust is slightly more intellectually demanding and takes longer to get into so has a a smaller monk like comm-- oh my god.
I'd argue that Rust is less demanding intellectually than C as you don't have to constantly worry about UB. C is definitely easier to "get into", if by "getting into" you mean writing unmergeable contributions full of unidiomatic code and security vulnerabilities.
C's age is not an issue in itself. The programming languages it replaced were ahead of C in many ways. It was a setback from a language design point of view, even 50 years ago.
>> C's age is not an issue in itself. The programming languages it replaced were ahead of C in many ways. It was a setback from a language design point of view, even 50 years ago.
C takes a different approach to how it handles problems that was described well in "The Rise of Worse is Better":
"Two famous people, one from MIT and another from Berkeley (but working on Unix) once met to discuss operating system issues. The person from MIT was knowledgeable about ITS (the MIT AI Lab operating system) and had been reading the Unix sources. He was interested in how Unix solved the PC loser-ing problem. The PC loser-ing problem occurs when a user program invokes a system routine to perform a lengthy operation that might have significant state, such as IO buffers. If an interrupt occurs during the operation, the state of the user program must be saved. Because the invocation of the system routine is usually a single instruction, the PC of the user program does not adequately capture the state of the process. The system routine must either back out or press forward. The right thing is to back out and restore the user program PC to the instruction that invoked the system routine so that resumption of the user program after the interrupt, for example, re-enters the system routine. It is called PC loser-ing because the PC is being coerced into loser mode, where loser is the affectionate name for user at MIT.
The MIT guy did not see any code that handled this case and asked the New Jersey guy how the problem was handled. The New Jersey guy said that the Unix folks were aware of the problem, but the solution was for the system routine to always finish, but sometimes an error code would be returned that signaled that the system routine had failed to complete its action. A correct user program, then, had to check the error code to determine whether to simply try the system routine again. The MIT guy did not like this solution because it was not the right thing.
The New Jersey guy said that the Unix solution was right because the design philosophy of Unix was simplicity and that the right thing was too complex. Besides, programmers could easily insert this extra test and loop. The MIT guy pointed out that the implementation was simple but the interface to the functionality was complex. The New Jersey guy said that the right tradeoff has been selected in Unix -- namely, implementation simplicity was more important than interface simplicity.
The MIT guy then muttered that sometimes it takes a tough man to make a tender chicken, but the New Jersey guy didn’t understand (I’m not sure I do either).
Now I want to argue that worse-is-better is better. C is a programming language designed for writing Unix, and it was designed using the New Jersey approach. C is therefore a language for which it is easy to write a decent compiler, and it requires the programmer to write text that is easy for the compiler to interpret. Some have called C a fancy assembly language. Both early Unix and C compilers had simple structures, are easy to port, require few machine resources to run, and provide about 50%-80% of what you want from an operating system and programming language.
Half the computers that exist at any point are worse than median (smaller or slower). Unix and C work fine on them. The worse-is-better philosophy means that implementation simplicity has highest priority, which means Unix and C are easy to port on such machines. Therefore, one expects that if the 50% functionality Unix and C support is satisfactory, they will start to appear everywhere. And they have, haven’t they?
I thought Rust didn't have a spec so everything in Rust was essentially undefined behaviour.
Has this changed or is the "defined" part still the compiler source code? In that case taking the source code of any C compiler as the _blessed_ one should get rid of any undefined behaviour problems as well.
You don't need a spec for the concepts of undefined vs defined behaviour. LLVM IR lacks a spec as well and is still built upon these concepts (LLVM IR does have documentation but so does Rust. There is no comprehensive document like the C specification for either).
Indeed the notion of what behaviour is considered undefined changes with compiler versions, and it is not fixed yet. E.g. mem::unused() for example is now basically always undefined and you are supposed to use MaybeUninit. But you get a warning if you try to use the old API.
This is for unsafe Rust however. With safe Rust, even though there is no spec, the guarantee is that, unless you hit one of the soundness holes in the language, or a piece of user code that uses unsafe internally, you are safe.
And yet hardly a day goes by without a rust story on hn and for the past 5 years it’s scored most loved language on the annual stack overflow developer survey.
That said, Rust has an exit from cult status and a starting path toward mainstream in its sights. If Rust makes it into the kernel then that is the beginning of Rust.
Wasn't there an article on here the other day talking about how Rust will never make it into the kernel as-is? The sentiment seemed to be that the kernel developers simply reject the idea of packaged code and many of the modern paradigms in Rust. I only had a chance to skim it but it made it sound like Rust in the kernel would be forced/relegated to a very different usage and style compared to the way it is written elsewhere.
Here's your problem. No, that is not an accurate summary of the discussion. And the whole point of the dialogue between the kernel maintainers and the Rust developers to figure out what needs to be done to make Rust suitable for inclusion in the kernel - which has already resulted to changes in the Rust toolchain and standard library. So
> Rust will never make it into the kernel as-is
Is about the most negative possible way to frame it while being technically true. Nobody is suggesting that Rust be included in the kernel "as-is", they're suggesting that Rust be included in the kernel, and having a dialogue about what would need to be done on both sides to make that possible.
Thanks. I’m not going to delete or edit my original comment, but I will say my question was meant in good faith without the intent to cast shade. I’m really surprised I was downvoted. I know next to nothing about rust beyond a fleeting hour or two I’ve spent with it, but I don’t think the way I phrased my comment was any worse than the off-handed remarks made in that article. Perhaps it’s neither here nor there but I’ve worked a lot lately. I’ve worked more than I care to admit for reasons completely beyond my control. I still try to keep up with stuff like this because I care. I love my work and this industry. I’m doing my best.
Not only that, the rust community also seems to have some weird complex where not only they evangelize their own language (which is fine) but also feel the need to bully and bash on other languages (usually targeting mainly C/Go but occasionally Java as well).
It's like functional programming in that both functional programming and Rust tickle just the right parts of engineers' brains.
I like Rust, I write a lot of it, but I can't help but feel that Rust isn't "it". I think another language will pave the path Rust trail-blazed, at some point.
Personally I think this comment explains why Go isn't the solution. It's magical for fast development following the most minimum and well trodden path. Stray outside and face demons at every turn.
We, like the commenter have made the decision to start slowly depreciating our Go code base in favor of Rust.
People are writing production level databases, distributed systems in Go... ofcourse it's a systems language unless your definition of a systems language is something super narrow.
There’s some places you can’t use a language with a large runtime like Go. For example, operating system kernels, real time embedded systems, game engines, anywhere you want to be extremely careful about memory allocations. You can write go for most of these use cases as toys and demonstrations, but this is where languages like C dominate and Rust/Zig are making inroads because of the ability to execute on bare metal with no runtime.
When I think of systems languages, I think of languages that are suitable for all of those use cases, but also for writing operating systems and code for embedded microcontrollers. Writing a kernel in a language with garbage collection is possible, but would it really be optimal compared to writing a kernel in other languages without it?
They're surely not talking about compilation times. Admittedly it's been a few years since I tried to build a web stack in Rust, but I felt like I was back in the 1990s dealing with the long compile times on modern hardware. Diesel was such a nightmare to use. It was poorly documented, and took a long time to compile even trivial example projects.
Compile times are still pretty bad, I recently upgraded from a pretty-new laptop to a blazing-fast desktop (12900K) just for Rust development. But at least they're acceptable now: using the mold linker, incremental builds take 1-2s on a large (~450 crate) project.
I agree that there's a lot more work to do before Rust development is pleasant on average hardware. Does anyone know if Cranelift is still going anywhere?
I really want Rocket to take off (no pun intended), but it reached v0.5-rc nearly a year ago, and has yet to even bump up to v0.5 proper. It's hard to feel confidence that it has the wings to be the future of Rust web given its current rate of development, which makes it hard to justify using in a work setting.
Does anybody have more insight into its development status?
We’ve been able to build a mobile/web hybrid ride-sharing app using Yew/GraphQL. A robot Lego-like 3D builder, with Yew interop with ThreeJS. And a full 3D web game with Yew hosting a Bevy game. Everything packed with trunk.
It’s still a bit rough around the edges but the Rust benefits more than makes up for it.
Does anyone else find it amazing how we are able to collectively rewrite entire ecosystems every time new language comes along? Web frameworks, network libraries, image libraries, parsers/serializers, etc.?
And the second realization is that the idea of polyglot programming has utterly failed. A lot is due to what kinds of APIs can be exposed between components, and that seems like an inherent problem of communication between different languages.
But there are other areas where you'd think we'd have wider agreement by now: the semantics of data types, runtimes, ABIs, memory layouts, etc.
I guess there are lots of reasons for differences. But it's kinda sad how much effort is spent re-writing stuff that already exists and works in a different language.
I'm happy to see some new Rust web frameworks that are doing away with attributes and returning to good old iron programmatic style. I might port some old plugins of mine from iron to Warp or Tide. Any opinions on which one to pick?
One use case I've seen is mostly-safe arbitrary code execution as a service.
Because WASM doesn't have a direct method of calling system API functions, you have quite a lot of control over the runtime. Most JS engines are too powerful to just run arbitrary code (especially if they're not run inside a browser) and other tools (LUA etc.) have libraries that usually assume that the program is not executing code from third parties. With many if not most WASM libraries, there is no unmetered networking, there is no I/O, only methods you explicitly write call wrappers for.
For tools like Cloudflare Workers, WASM can be used as a format that executes relatively quickly, relatively safely. Most software won't be in a position where you'd want to push third party blobs of code and execute it in the backend, but for some programs (SaaS + custom report generation?) it can be a nice plugin platform.
There are, of course, alternatives, but since LLVM and GraalVM compile to WASM, there are tons of languages that can generate a compatible blob.
There is something to be said for code reuse, but the speed of the WASM <-> JS bridge to interact with the DOM makes that use case quite difficult to accomplish efficiently.
Using the same language for your frontend and backend has a lot of advantages in terms of code reuse and it's potentially easier in terms of the number of skills your team needs.
I think most low latency tricks work just as well in Rust as in any language. Shared writeable buffers to keep allocations and such down can be a challenge, but with the right unsafe code (and knowledge of the workings of Rust) you can make them work.
I think what it comes down to is that I'm just not that into bondage and discipline from a compiler. Yes, I know, it's trying to make my code 'safe', and I'm horribly cavalier and I should feel bad, but:
1. the borrow checker rejects valid programs, has a lot of corner cases it can't catch, and is in active development
2. people routinely write more inefficient code to satisfy it
3. people routinely freak out about 'unsafe', which is a bit much given the first point
Anyway, YMMV. Maybe you're smarter than me, or rust fits your mindset better, or you enjoy asking for help on discord all day, but I am so glad not be using it anymore.