Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Is Rust Web Yet? (arewewebyet.org)
130 points by tomhaines on April 16, 2022 | hide | past | favorite | 174 comments


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.


You can unsafe to get around the borrow checker, just not directly; you have to go through raw pointers.


> I spent about the past month learning rust

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.


Maybe you can rewrite his statement as "your code is not idiomatic to Rust"?


shrugs

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.


Probably not, I am afraid.

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 :(


> has a lot of corner cases it can't catch

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.


> the borrow checker rejects valid programs

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.


I think it's more of a Rice's theorem/ halting problem issue than an incompleteness theorem issue.


> the borrow checker rejects valid programs

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".


One charitable way to view Rust's philosophy is that it's better to catch bugs at the compile stage than at the execution stage.

In theory yes. In practice, getting to the execution stage was such a slog that it rarely happened.

Would have liked it a lot better if the borrow checker had the option to be -Wall instead of -Werror.


Out of curiosity, what are your preferred language(s) to write in, and what sort of software were you trying to write?

> Would have liked it a lot better if the borrow checker had the option to be -Wall instead of -Werror.

Wouldn’t this defeat the purpose of the borrow checker and remove the guarantees the language gives you?


Most enjoyable languages for me are definitely GC'd (JS, TS, Ocaml, Ruby). But I have professional C++ experience.

The software is a layer over an in-process key value store written in C (LMDB).


That's been every language's philosophy for generations. (Obviously, some languages do a better job of it than others).


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.


Ditto with my journey from Warp to Axum. The Rust Playground now uses[1] Axum as its backend and I’ve been pleased with working with the maintainers.

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


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.


Mypy together with this plug-in gives you typing for django. https://github.com/TypedDjango/django-stubs


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.

What I've gained as a result:

- execution speed (about 3x faster, single-threaded)

- better-documented data structures

Things I've lost:

- ease of iteration

- concise, readable code

- really smart type inference

- 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.


My impression has been that python is easier to iterate on over the course of a week, while Rust is easier to iterate on over the course of a year.


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.


Nope, it's written correctly as-is. Probably the "time to tests pass" is about equal, but the iteration and maintenance is far better in Rust.


> but the iteration... is far better in Rust

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.


Check out pip install -c constraints.txt for the equivalent of lock files.


We use constraints.txt, which helps a lot. It's not quite a lockfile but it's something.


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.


How so? We use many of the crates listed on the linked page - sqlx, rusoto, actix-web, tower/tonic, etc.


Yeah, but the thing you're building is quite a bit more complex than the average consumer-focussed website.


I guess, but if you're just building some basic CRUD app that's never going to change, why would iteration speed matter anyway?


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.


I disagree on the things you've lost.

> - concise, readable code

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.

> - ease of iteration

At the cost of introducing a bug/technical debt.


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.

> At the cost of shipping a product!

Okay!


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.


Sounds like what you want is how exceptions behave in most languages? Including c++?


> 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.


Wait... You're rewriting the PHP regex code instead of using the regex crate?! Just...wait...why?

Are you using lazy_static? See: https://docs.rs/regex/latest/regex/#example-avoid-compiling-...


Any reason why you can't put the Regex in a once_cell and then keep on applying it?


Rust noob here but couldn't you write

  if let Some(element) = some_vec.first() {
    println!("{}", element); 
    // .. more code
  }
and avoid the unwrap and the empty check?

Edit: Added a `let` I had forgotten.


Exactly (well other than "if let" here.)

Every time you have a Boolean if condition with an unwrap, it means you are not taking advantage of the type system.


Thanks, added.


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.

The PHP version can also be written as

    if (($element = reset($some_arr)) !== null) {
        echo $element;
    }
But that code is similarly divorced from the imaginary pseudocode equivalent


> 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.


You can usually use pattern matching to get around a check followed by an unwrap. I would rewrite your example as:

    if let [first, ..] = &some_vec {
        print!("{}", first);
        // .. more code
    }


Thanks, I did not know about that particular solution — that adheres more closely to the idealised pseudocode than any of the other Rust alternatives.


That sort of "flow typing" is extremely cool and I'd love to see it added to Rust. Perhaps something like mypy's proposed TypeGuard.


> Rust code is significantly more readable.

You should put down the Rust kool-aid. Rust is incredibly hard to read due to its sigil and abbreviation heaviness.


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.


No, what I mean is https://github.com/rust-lang/rust-wiki-backup/blob/master/Si... and that's just pointers, you have #[] #![] and then three ways to invoke a macro !() !{} ![] and of course a lot more.

Maybe all this is necessary for a system level programming language, fine, but don't tell me this is easy to read.


> but don't tell me this is easy to read.

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.


I have no problem telling you that Rust is extremely easy to read.


Oh, and the @ ones don't even exist anymore, haven't for like a decade afaik


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.


I guess I didn’t see it that way, then again I’ve been bitten by C++ which is crazy land for overloads


>really smart type inference

Are you calling php's chaotic mess of type juggling "really smart type inference"?


The 3x speed increase is interesting. Do you have any further details?


Only 3x speed increase feels like either did PHP a very good job or the port is not optimized for rust yet.


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.


what's the total running time? ms/sec/minutes/hours? is it heavy IO bound?


A few minutes.

It's not heavily IO-bound — it's mostly memory-bound (it's a type-checking tool for very large codebases).

Further (painful) optimisations will likely involve the use of arenas to prevent so many atomic de-allocations.


> Things I've lost:

> - ease of iteration

> - concise, readable code

Ouch... then it's a big nope for me.


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.


> Yes! And it's freaking fast!

Going by these benchmarks (click around to find your own use case) Rust is definitely fast, but not (much) faster than many other languages, including Javascript. https://www.techempower.com/benchmarks/#section=data-r20&hw=...

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.


If you look at the techempower submission source code you'll quickly see why: https://github.com/TechEmpower/FrameworkBenchmarks/blob/mast....


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.

[0] https://github.com/giltene/wrk2


Please explain how we are to practically identify "idiomatic, standard code that a slightly above average developer would plausibly write."

Or is it some Goldilocks not-unoptimised not-too-optimised I'll-know-it-when-I-see-it wishful-thinking thing?


Those benchmarks are pretty heavily gamed. Think about things like hardcoding "Content-Length: 123" instead of computing the length at runtime.


I wish benchmark suites would reject those kinds of shenanigans, it's completely ridiculous and makes the results practically worthless.


Do you raise those issues as specific bug reports against specific programs?


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.

Apparently async db requests and batching transactions are the key to winning on that benchmark: https://news.ycombinator.com/item?id=24315512

I wish more benchmarks would produce latency vs requests/second curves in microseconds. I bet the latency is massive due to batching.


There fortune test is weight 1.5 while others are weighted at 1 or .75.


The code used for it looks like someone ran a compiler over it and then dumped the output lol it's... extremely hard to read.


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.

You read see the source of the tests here: https://github.com/TechEmpower/FrameworkBenchmarks/tree/mast...

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.


A huge amount of time and money has been spent making JS as fast as it can be.

Additionally a lot of these benchmarks focus on things that the JS engines are very good at. It isnt like they are running CPU intensive tasks in JS.


The author of just-js did a write up about how he got that performance: https://just.billywhizz.io/blog/on-javascript-performance-01...


"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?


That’s correct, the creator has been uber meticulous and methodical in his versioning and using stable Rust parts.


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.


Rust versioning is different than the custom in lots of other languages. There are lots of perfectly good mature 0.x libraries.


Rust definitely loves a good zer0ver, for better and worse.


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).

[1]: https://github.com/prql/prql [2]: https://lang.prql.builders/editor.html


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.


Axum is an excellent web framework. I'm writing a tutorial ebook here: https://github.com/joelparkerhenderson/demo-rust-axum/blob/m...


Can some explain why I always get cult like vibes from the rust community?


Imagine your job is hammering nails. Eight hours a day for twenty years. You’ve hammered 403,286 nails.

And then someone shows up and hands you a nail gun.

Rust makes me happy because we speak the same language. Stuff gets done and I go home at 4:30 and play with my kids instead of my compiler.


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'm not sure what you mean by manually manage.

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.


Ah, but there's the trick, you ignore the presence of potential UB and write a lot of complicated code real quick!


>> 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?

Unix and C are the ultimate computer viruses."

Source: https://dreamsongs.com/RiseOfWorseIsBetter.html


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.


Rust is safe. That is huge. The CVE vulnerability list would be like 1/10 the size if Rust were introduced 30 years ago.


Go here: There can only be so much free coke at the Gopher meetups, so we promote Rust to throw people off and keep the numbers down.


Because Rust has a vocal community and that noise far over represents its usage here on HN.

People aren’t searching for Rust much:

https://trends.google.com/trends/explore?q=%2Fm%2F0dsbpg6,%2...

Because it’s not a popular language, cobol and prolog are more popular: https://www.tiobe.com/tiobe-index/

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.


>I only had a chance to skim it

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.


Because the world is really ready for something to displace C and bring low-level programming to the 21-st century.


Go


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.

https://news.ycombinator.com/item?id=31019234#31024525


Go is closer to old versions of Java than C.


Go has a fat VM with GC. It’s great for lots of things but is not a systems language.


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?


not 19th century. 21st century.


Is there a good new tool that does not develop a cult around it?



And for the machine learning/data science domain:

https://www.arewelearningyet.com/



How do you debug rust code that has been compiled to we assembly?


Source maps I suppose.


They still don't work very well if you use wasm-bindgen: https://github.com/rustwasm/wasm-bindgen/issues/2389 (consider voting for it).

P.S. It's very hard to work without wasm-bindgen, since you have to wrap browser API manually otherwise.


> "Yes! And it's freaking fast!"

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?


In fairness to rust, compiler speed has gotten a lot better. Can't speak for every crate though..


Some web frameworks are much worse about compile times than others. I've heard Actix and Warp are not great at this, and that Axum is much better.


> They're surely not talking about compilation times.

Correct.


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.


The api and cli of https://mayhem4api.forallsecure.com/ are written in Rust. Still using react on frontend with REST api written with actix.


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?


Looking forward to checking some of these frameworks out.

Question- Curious to know what backends / languages would be the fastest for a REST-based PostgreSQL backed API?


I love ironic tone of this post. In funny and twisted way author explained how poor rust for web is. Thanks for making us aware :)


So, are there actual uses (not just potential use cases) of webassembly beyond games and crypto-mining?


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.


yes. WASI has the potential to replace containerized services (ie. docker, k8s).


Is Rust Web3 yet?


there is also fork of actix-web https://github.com/ntex-rs/ntex


What's the best way to learn rust for low latency programming in 2022?


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.


Rust is not a programming language it’s a syntax error.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: