Using Rust at a startup: A cautionary tale(mdwdotla.medium.com) |
Using Rust at a startup: A cautionary tale(mdwdotla.medium.com) |
I would love to hear what tech stack (or stacks) the HN community thinks currently allows a small team to move fastest for this type of product.
I guess nodejs might also be throw into the party, as it is anyway needed for many SPA frameworks, and for better or worse, Go, given its relevance on DevOps space.
Second tier, a bit higher performance, yet somewhat less productive: ASP.NET Core; Spring Boot; Node/Express/Nest; maybe Go
- Laravel
- Rails
- Next.Js or similar
- Python
- C#/Kotlin/Go
Add login and auth and stuff via ory/keycloak
Great point
Part of this depends on one's bar for quality. In Node.js I could write `JSON.parse(input).foo.bar` really quickly. In Rust, I'd probably write two struct definitions with `#[serde(Deserialize)]` first, then a similar line. It'd take 45 seconds longer, but when I'm done, the Rust program validates the input and prints decent error messages.
I like that this is enforced in Rust (and that many other things are well beyond what tsc guarantees), and I like it in Node too and I’m glad that a certain corner of the ecosystem takes it seriously.
If you were in typescript, zod would also automatically generate your interfaces too.
Does it match the experience of a team and its need to collaboratively deliver software quality across varying levels of engineer experience? If not, then I'm not sure your individual experience is pertinent to the subject matter at hand (using Rust at a startup).
If you don't need performance, you might be better off using any number of safe, GC languages. (That said, I actually find the ergonomics of Rust nicer than most other languages. A rust-like language with a GC would be very nice for web backends imo.)
This is a problem in general, but it's not a good reason to use Rust in a startup, it's a good reason to use Java or C# or TypeScript or another well-worn high-level language with static typing.
Rust's niche is solving a set of security problems that most high-level languages don't have. Choosing it for a startup only really improves your security if the other language you're considering is C or C++.
Then you talk about why "startups need regulation" and the lack thereof is why "the world remains an insecure mess" which is another non sequitur drawn from statements which, while individually correct, would never combine to imply that conclusion.
The world is an insecure mess because some societies have not collectively agreed that it is more expensive to be insecure than secure.
Startups don't "need" regulation anymore than regulation is a cure for any problem. In practice, poorly drafted regulation exacerbates rather than solves the problem it is scoped to solve because it opens the door for regulatory capture based monopolies.
That said, I do agree with parts of many of your individual statements: more effective regulations would be good not just for citizens but for startups and society in general; memory safety oriented code tends to be higher ROI for everyone; the world remaining an insecure mess is a pox on global societies whereupon well deployed investment into secure infrastructure would produced significant ROI compared to the present state of the world.
that seems a little alarmist, don't you think?
This could be true for C++, Java or maybe C#. Against them, python/ruby run circles.
But Rust change the equation.
Is super-productive... BUT what is important to note is that you need developers that have done the initial climb and go fully rust with it.
After this, things start to click neatly:
- Modeling code is easy & fast
- Composing code, flow!
- Testing (all of it, including benchmarking and integration) flow.
- Refactoring (big!) is super-easy. This one is where the "velocity" is actually need.
But you CAN'T "code fast". You CAN'T skip in good practiques "let's ignore the use of clippy, Result, proper use of traits (like Into/From), etc". (and even write documentation with including doc-test!)
This is where the "I write code FAASSST bro!, not time for pesky good practiques!" get hurt with Rust.
You need to buy the whole package, because Rust is MADE to be used fully in harmony.
And when you finally do it. IS FAST TO CODE.
And even FASTER to refactor.
And probably so minimal post-fixes.
---
P.D: Major blow against Rust? Slow compiling. This is the one factor that blow the dream and where you truly need a decent machine and flow to make it nice. However, if you take the above advice, you are already with Test/DocTest/Clippy/Check flow and that part can be done to be efficient.
I really dislike python in production and have done it a lot more than I've ever wanted.
Are you actually a C++ programmer? I work at a firm that uses C++ and Rust and this isn't the case at all. Setting up dependencies, tests and benchmarks is a one-off cost, most C++ compiler errors are quite understandable to experienced devs unless they're doing something really hairy, and C++ has algebraic datatypes, as a library (std::variant) via variadic templates, something Rust doesn't support. For C++ devs comfortable with template metaprogramming Rust is missing a bunch of features, many things that are relatively simple in C++ are literally impossible in Rust (at least without writing macros). Rust also doesn't save much on debug time if writing single-threaded code, because memory errors are a very small proportion of the bugs one encounters in modern C++ written by experienced devs, rather most bugs are logic errors, which Rust doesn't prevent.
If you're willing to blindly follow the suggestions given you by the compiler about references, and clone() things when it doesn't seem to work, you'll be just as productive as a Python/Java ... programmer. You'll just not be as memory efficient as a Rust programmer who actually groks that stuff. That's the tradeoff: coder productivity versus memory efficiency.
I feel like 90% of my struggles with Rust in the real world have to do with the borrow checker. The two pieces don't feel related enough to me that they need to be interlinked.
This sounds just like all other projects that don't keep the code professional.
The author complains a lot about the quality of the Rust libraries and documentation, but I bet that their code base and documentation and especially testing is worse.
Still, I agree that Rust is not ready for a startup where the requirements change often.
Naturally, there is a reason people opt for higher level languages like Ruby/Python or at least Go or Java/Kotlin to write apps like this.
I understand why Rust is trying to shove its way into the CRUD/web space--that's where all the programming is. I just don't understand the converse.
And, well, the usual, "learning curve" and some largely hypothetical concerns about hiring. I developed a complex app with Rust with very high time pressure and with developers that didn't have any previous Rust experience (I didn't have much of it myself), it was risky but it worked out well, so I know first hand that it's possible. And we are not "ex-google"!
(Not /s, I genuinely think it's cool things are like this now)
- Rust tends to push you to make good decisions. In my case, one of these good decisions was to ditch an ORM (which has always slowed me down) and instead write Postgres queries directly via SQLx. The compile time checks against an actual DB helped my speed dramatically
- Free performance can be a really big help when you are trying to figure out an API or algorithm. It's really helpful to know that my unoptimized code won't tank the server, and also helps me save costs on the cloud.
- "It doesn't compile, or it doesn't break" is kinda a mantra for Rust, and really helps me focus on problems at hand, instead of hunting down bugs.
> You will have a hard time hiring Rust developers.
I've found that there is a drastic difference between hiring devs willing to learn Rust, and devs that want to work with you because you are using Rust. The bar of those devs that seek out a Rust position tends to be very high.
> We made a huge mistake early on by adopting Actix as the web framework for our service...(To be fair, this was a few years ago and maybe things have improved by now.)
Actix-web has gone through some version-churn, but it's never been "buggy" in my experience. The experience multiple years ago is vastly different than today, but even my older services written years ago with Actix-web are still running fine.
> Libraries and documentation are immature.
Perhaps in years past, but I've always found the docs for Rust and its libraries to be very good. Folks write entire books on elements of Rust, and the standardization of the display of crate docs keeps things consistent.
> Rust makes roughing out new features very hard.
The "json!" macro can come in handy here. Also Github Copilot is a godsend for this.
> What really bites is when you need to change the type signature of a load-bearing interface and find yourself spending hours changing every place where the type is used only to see if your initial stab at something is feasible. And then redoing all of that work when you realize you need to change it again.
Hours? These type of corrections are spoonfed to you via errors at compile, or via the IDE. I've never had to spend hours on this. Everyone of these changes could be a bug, and having a typesafe language is a huge help here.
I recognize I might be in the minority, but I've really enjoyed using Rust for services for my startup. It's helped me move fast, but maybe I'm a special case. I'm curious to hear other's experiences.
You should be using the language you know best or dynamic languages when you are trying to move fast.
Jokes aside. This is a thoughtful and interesting article. I was wavering on going down the Rust path for my SaaS start-up. Glad I didn't in the end for exactly the same kind of reasons.
The goal is to deliver products and iterate as fast as possible. Productivity is key to success.
IF (and it is a big IF) your startup ever gets to the point of having to scale up (nice problem to have) then just throw money at AWS, hire senior engineers and let them figured it out.
What matters is customer experience; nobody cares of what’s behind the scene.
Not all languages are as bad as Python, and Rust comes with a lot of other costs. But it's very seductive to think about a world where you build a static binary with a single command, the build always works no matter what system it's on, the binary always works on the target OS regardless of what else is or isn't installed, it will use 1/10 the memory, and most likely it will never have any runtime errors because you were made to handle them all already
(I realize Go has at least some of these benefits, but its type system is hard for me to stomach)
Can you elaborate on the issues of Go's type system?
A note on performance, Rust's the only langauge where I haven't had the need to update my unit test harnesses to `TRUNCATE` tables instead of creating/discarding a separate database per test on PostgresSQL.
I'll also like to mention the gem that is SQLx[1]. As someone who's never been satisfied with ORMs, type checked SQL queries that auto-populate your custom types is revolutionary. With the error-prone langauge-SQL boundary covered, I was surprised just how good it can get making use of the builtin PostgreSQL features. Almost to the point that amount of effort the community's put to building great tools like Prisma.js and <insert favorite ORM> feel like a fool's errand (at least so for PosgreSQL).
[0]: https://github.com/alexpusch/rust-magic-function-params
[1]: https://github.com/juhaku/utoipa
[3]: lib.rs/crates/sqlx
I often reach for Rust when Python could do, but it’s a bit bigger than a one page of code program. I also forget I’ve been doing Rust nearly full time since late 2015. And before that 15 years of C and understanding memory, etc. Maybe we’re just a rare breed.
Well, Rust uses macros for that which are equivalent to C++ templates. Ever used println!() ?
In my experience writing a Zod schema to parse something into a typesafe form is quick and easy and you end up with full maintainability
- you actually care about type safety so you’re not going to just `as any`
- you have a linter that will block unsafe commits or merges
- you have some type you want to use after it’s established safe, and an idea you want to fill out without breaking flow for even those 45 seconds or whatever to define your schema
- despite your predilection for type safety, you iterate on ideas like that the way people who prefer dynamic languages talk about: damn the torpedoes, I’m in the REPL
Given those assumptions (which fit a lot of TS devs well), the really good thing about @ts-expect-error in particular is it allows you to save an intermediate step for later, catch it in CI or whatevz, and it'll yell at you when, and only when, you leave it unsafe unnecessarily. It’s a good way to have your safety cake and eat your dynamic prototyping cake too, especially if breaking flow for a yak you’d shave anyway is going to sink your flow.
Most bugs are from the programmer having a misunderstanding about how the system works as they change it. The stronger the type system, the more potential misunderstandings can be verified statically by the compiler before they reach the user.
Other, dynamically typed languages, will require either having unit tests, or running it in prod to find that user who has a null email or whatever, and results in things going awry. Since we're talking about startups, there are of course no unit tests or users, so this is all largely academic. Like, if there are 0 users to trigger bugs, and a bug that could be statically detected by a good type system exists, does it make a sound?
I'm bringing this up because in discussions like this, people tend to play fast and loose with the definitions of "safety". The kind of safety we're talking about in a decision between Rust and Java is mostly an externality to the SRE team, not to customers --- in other words, not an externality at all. An internality, if you will.
I've decided to pick an arbitrary list of security issues where the fixes will be visible to gain some small anecdotal evidence. The top result for "gitlab CVEs" is this august release announcement, let's look at the first three vulns on it: https://about.gitlab.com/releases/2022/08/30/critical-securi...
1. Remote Command Execution via GitHub import
This one was a typeing issue where an object with special keys resulted in dynamic code execution. That could not happen in rust. See the hn discussion here - https://news.ycombinator.com/item?id=33155527
2. Stored XSS via labels color
This can be made into a type-system issue with a good enough type system, text and html should be different types. Arguably this could happen with rust, but honestly, probably would not.
3. Content injection via Incidents Timeline description
This one is also arguably a typing issue for the same reason as above.
----
Hey, look, 3 errors that type systems would help with and which had security implications.
> it's much less clear that stronger type systems reduce vulnerabilities; in fact, the evidence mostly cuts the other way.
What do you mean by that? In what case is a worse type-system (like java or go) going to make it harder to write vulnerabilities than a stronger type system (like haskell or rust)
However, type correctness won’t necessarily prevent a DDoS, stolen password, specter, timing attack, phishing attack, etc. Type safety is a nice-to-have but not sufficient for security in SAAS products.
As a consumer, I tend to prefer the thing that is available a bit later (or more expensive) and works well, versus the one that is available sooner (or cheaper) and is less reliable. Maybe that preference is a result of years spent fixing things that could've been designed better?
Many people would argue you with you that the "easier" programming languages are no less reliable, context depending. Framing this in the context of the article a CRUD app, there are plenty of reliable frameworks in many languages.
I'm the thousandth person to suggest this, but my ideal language for web servers and lots of other things would be Rust with a GC instead of a borrow-checker. But Rust has its unique killer-feature to thank for a lot of the traction it's gotten, so it's very possible this hypothetical language never would have taken off in the first place. We've got what we've got, languages are social constructs just as much as technical ones, maybe even more so
There are exactly two types of vulnerabilities Rust (and some other modern memory-safe languages) mitigate beyond memory corruption:
1. Java, Python, and Ruby have deserialization libraries that can easily be misused to create RCE vulnerabilities.
2. Python, Ruby, and Javascript have eval and eval-equivalents (which is essentially what your first example is).
You can pretend anything is a type system issue, but neither of these two vulnerabilities are properly understood as type safety errors. Java has a sharply more prescriptive and policed type system than Ruby does, but both have deserialization issues; it's just a generational thing.
Deserialization in new Java code is unlikely; deserialization is much less common than SSRF, which plagues Rust code just like everything else. In 2022, there is no meaningful security benefit to Rust over Java.
There are other reasons to use Rust! People should just stop making up fake security reasons to do it.
It's a good reason to not collect/store information in the first place. If your startup depends on the collection of your user's PII, then I'd seriously question if there's a real purpose to your startup. IF your startup 100% does require the use of PII, then yes, slow the fuck down, and do it right.
If you're some other startup that doesn't actually need PII, but realize it is a fast way to make money, then you should really soul search to ask if you're as amoral as you look to others and if you're okay with that. If not, make a better product that doesn't mean selling your soul to make a buck.
> And this goes well beyond Rust and "mere" memory safety: this extends to every kind of taking things slow and being careful in your coding rather than just throwing something together and later finding out you've made a serious error.
That matters because that's the kind of "safety" we're talking about when we discuss externalities for end-users --- not servers that need extra monitoring because they might crash.
Java is more left-pad resistant than Typescript, granted.
Idiomatic Rust code is just as cavalier regarding NPEs as Java or Python code. Because the language starts to look really gross when you make your code panic-safe. So you have an `expect` here, an `unwrap` there, and now you're going to have the exact same runtime issues as the more expressive managed languages. No Rust programmer thinks their `expect` will panic, just like no Java programmer expects something will be null. Rust does not force you to "exhaustively match".
I will also push back on other commentors here saying Rust is not good for web apps and APIs, and I have found that to be the opposite of true. I read Zero To Production In Rust [0] (a great book by the way, all about creating a web API with actix-web and Postgres) and deployed an API that has not gone down a single time since deploying near the beginning of this year. It's as ergonomic as (and sometimes even more so than) any NodeJS or Go API I've made (as even though Rust doesn't have function overloading, actix-web and others have some macro magic they do behind the scenes to make it appear as if it does), and there were very few times I had to contend with the borrow checker. If there had been and if I were really going for speed, I would also have cloned everywhere that was needed.
When I write something in Rust and it compiles, I can depend on it to work, and to continue working. At the end of the day, use the tool that makes sense for your business, but I don't think Rust was necessarily a bad choice based on my personal experience with it.
I actually think that, ironically, the if err problem with go is worse for reading code than writing code. If you are used to the pattern it's not that difficult to add the `if err != nil` after a fallible function call, and as is mentioned elsewhere, linters can help catch if you miss it. However, if you are trying to figure out what a function is doing, it can be very difficult to follow the flow, when 3/4 of the code is
if err != null {
return err
}
especially if you don't have a lot of experience reading go code. And that 3/4 is not an exaggeration. I have had to read functions where literally every line of normal logic is followed by those three lines to propagate the error from the other function. A function that would otherwise fit on a single screen now takes up over three screens worth of scrolling.> God I get tired of the generics criticism with Go. I truthfully don't even notice it when I write Go, I don't understand why folks get so bent out of shape over it.
Over ten years later, Go gets generics.
I actually like Go over Rust, but really, really wish the language provided some more ergonomics and fixed several of its terrible gotchas. (the for loop issue for example)
Also wish Go gets RAII some day.
I don’t think that’s any better in Rust though.
After going through the book, I've found that I simply use the same boilerplate for every new project, so it's more of a `write-once, use again` type of deal. In that case, I don't worry about the time it took to get to the point where I could do that reusing.
I’ve worked my way through a couple of rust books, but this was the first one that actually talked not just about the language, but the entire development flow. Git workflows, CI/CD, docker, comprehensive testing.
Even after clearing the initial rust fundamentals, there are so many standard libraries like anyhow, chrono, serde, thiserror, and tokio that are almost required knowledge, but aren’t covered in most books.
Thank you for sharing your discovery!
Not that this takes away from your point, but does Go have function overloading? I had assumed it hadn't given the discussions I had heard about what it would take to retrofit `Context` parameters on everything when they added that type, but I guess maybe this could be one of those "functions can but methods can't" things (which I think I heard is how they do generics as well?)
That's going to get tiresome after about half a day.
Exceptions or GTFO.
Result<> or Go returning many values is wayyyy better.
There's also a bit of annoying typing stuff involved, but generally with something like the anyhow crate you can handwave that away too.
In fact, you can often return Result from an underlying function from your own, achieving an effect similar to exceptions.
Personally, I actually think it's a worse fit for things like kernel/embedded development than for web backends. The ergonomics of Rust are so Javascript-y that the web is natural, and other environments where C is used are a lot messier and trickier.
However, it didn't have much support after TypeScript took the JS world by storm. If it had, we might have seen the same great Rust-like benefits on the frontend as well, and perhaps the backend too instead of NodeJS.
There were just a couple of failures in the first 2 months.
How often does the codebase change? How many people work on it?
Its dumb when the C++ community refuses rust when it guarantees memory safety, so why would you bring in a language you prefer to an area that doesn't need it if it introduces that issue?
If you like Result<> or concurrency, use a GC language with those things. Go may not have them, but your choice isn't just Go.
IMHO C# is also an excellent choice, but there's a lot going on behind the covers to keep it working properly in a multi-threaded web environment. This mostly doesn't concern you as a developer, until it bites you.
This is really the main reason not to use Rust when you need to move fast: if you don't know the language yet, it will take you time to learn it. But that's true of any language. You probably shouldn't do your startup in Java, either, if your team isn't familiar with it.
Yes, Rust's learning curve is steeper than many other languages, but still... if you're an early-stage startup and you need to move fast, write in a language that you and your team already knows.
Java is wildly easier to pick up than Rust, though. Hordes of productive enterprise java programmers are employed today who frankly don't even understand what memory safety even means, much less the tradeoffs their language runtime (or software) needs to make to ensure it.
It's certainly a truism that you should tailor your development environment to the talents of your staff, but that doesn't mean that all tools are equally applicable. Would you do a startup today using APL or COBOL?
I think Java is a little easier to pick up than Rust, but not much. And this is especially true if one is not already familiar with Object Oriented programming. Classes are weird - conflating a number of concepts that really ought to be orthogonal to each other (and I remember thinking so as a beginner, although I couldn't articulate why then). IMO, structs, enums, and traits are much easier to explain to a beginner.
Rust does have the overhead of needing to care about ownership and memory, but I don't think that's as significant as you might think (and Java also leaks some of these details in for example the difference between objects and primitive types), and it partially makes up for it in other areas.
As an experienced Rust dev, I had the opposite problem. I looked for a good Rust job, but couldn't find one. Took an Elixir job instead.
I also don't get the complaint about lack of productivity. I've worked in a dozen languages, and Rust is still the most productive language for me by far. I feel like they're speaking from inexperience here. I'm slow whenever I start a new language too, and Rust was no exception. Keep at it, however, and it'll pay off in the long run.
Did you work on it with a team in a serious production environment?
Sounds like you struggled to find a Rust job, so I'm not sure how you can know that without really betting on it like these guys did.
I did. I left because management was a brood of compulsive liars.
I think this should be at the top of the post. Rust seems like an obvious bad choice for this. For something not performance-sensitive that doesn't require most of Rust's power just use languages/frameworks that are made for this kind of thing. Of course you can do this with Rust, but you're probably picking the wrong tool for the job. The Rust ecosystem around building typical webservices isn't nearly as good as it is for other languages, and the benefit you get from Rust's safety and performance is worth less than the iteration speed you gain from the standard Node/Python/etc approach.
A CRUD/SaaS web team should only be using it for high performance areas with a few microservices. At least until the web side of Rust significantly matures (mostly in the people/community side not just code).
But I've used Rust mostly for performance-critical CLI tools, or highly specialized servers. Typically, these have a narrowly defined job that must be done reliably and efficiently. Rust shines at this. These tools run reliably with almost no changes, often for years at a time.
But I have learned that it's often less useful putting policy and frequently-changed business logic in Rust. That kind of code is often accessible to more developers in a strongly-typed scripting language.
I assume that you’re thinking of TypeScript or Python with type hints.
Do you mean that these languages are more accessible because they are more widely known by developers or because codebases in these languages are easier for some reason to change frequently than codebases in Rust?
One of the tradeoffs of programming in Rust is that you need to distinguish between value vs. reference, stack vs. heap, and a number of other choices like that. This helps performance tremendously. But unless most of your team is comfortable making these decisions, they add a modest tax to all Rust code written. At least compared to TypeScript.
Rust is a fantastically productive language now that I know it well. There's definitely a point on the learning curve where Rust can be competitive with several popular scripting languages. But getting an entire team of programmers to that point takes a concerted effort.
So for many teams, Rust is the biggest win for problems like:
- "I need a server that handles a few kinds of specialized requests at high speed."
- "I need a CLI tool that can process 60 GB of input data on a regular basis."
- "I need to parse this file format that involves 197 special cases that I must keep carefully straight."
I just haven't found it to be the case that developers have a very hard time learning it, but we haven't grown to the point where that would maybe be the case. We also lean heavily into microservices so "oh there's no library in Rust but there's one in Python" is low cost, we can just write a service in Python.
It's remarkable how significantly the first few months of a project impact the first few years of a company. How quickly casual & spontaneous decisions, largely based on convenience and familiarity, ossify into legacy drag.
- C/C++ developers like it as an alternative for low level languages
- Functional programmers like it because of it's functional features
- "Cargo Culters" / "Hot Language" / "Flavor of the Month" followers like it, as it's in vogue at the moment (this is the crowd that has likely shifted from various web/API scripting languages over the last decade - (php|ruby|python) -> nodejs -> rust)
However, as the article alludes to, for the third crowd, Rust is just likely not a great fit (even if it has a lot of neat features and introduces some great concepts others may not stumble on of their own accord) when your implementation doesn't do much beyond web dev/API/scripting purposes.
While Rust isn't simple, I don't know any other languages that fill this space.
With a competent teacher, experienced devs should be able to pick up on the memory model pretty quickly, and that is really the only initial blocker to productivity. After that a dev can essentially write procedural code if they want, like other common languages, while refining their thinking and practices towards idiomatic Rust.
Well, that's because linked lists are not simple. The linked blogpost makes that clear enough.
1. I'm familiar with lifetimes and they don't slow me down anymore. Also, I usually structure my projects such that I don't need to explicitly express them.
2. I spend a lot less time fixing bugs because Rust makes them impossible.
The wasted clock cycles and developer hours more than makes up for the slow down in productivity rates we’ve seen across advanced economies lately!
> This project was a cloud-based SaaS product that is, more-or-less, a conventional CRUD app: it is a set of microservices that provide a REST and gRPC API endpoint in front of a database, as well as some other back-end microservices
Of course.
Use Java/Kotlin or Node.js/Typescript or Go or Python for your basic web services.
Easy test: Would you at least seriously consider using C/C++ for it? Only then should you use Rust.
I'm a Rust-stan for sure, but I probably wouldn't use it for this task either as it'll be hard to find other people who know how to use it - and also for this task. Here's hoping that changes, though.
[edit] I might also add it's not a good fit for making these types of endpoints at a startup as the goal of a startup isn't really to write perfect/correct/safe code, but rather to ship a product as fast as you can so you can iterate on the product-market fit. Quick and dirty in another language is probably a better fit for the business case. Rewrite it later.
1. The risk of future unsafety (a GC language will always safer than rust)
2. Faster development that's easier to change on a dime.
3. No risk of memory leaks (which can happen in safe rust)
Ruby on Rails certainly isn't perfect, but it's a highly battle-tested web framework. It's been so widely used for so long that you can have high confidence that you won't run into any bugs that will require digging into the guts of it. You can write code against it and be highly confident that any bugs or misbehaviors are in your code, with difficulty of finding and fixing dependent only on how good the code you've written is.
With Rust, I don't think that's the case. The DB and web server related libs just don't seem to have that much manpower behind them, and haven't had big complex services built on them in production for years. If you want to do anything mildly complex, there's a good chance you'll have to dig into the guts of these to figure out what's really happening or to resolve some weird bug or add a feature. That'll require a lot of skill, even if the code you're actually writing is mostly straightforward.
If you've ever generated some object out of some factory and then tossed it on a list of things and then gone back to generate a new object out of the factory and found that the first object you put on the list got scribbled over because you forgot to clone it, then that's a category of bugs that rust entirely prevents.
And I thought I was pretty good about reasoning about data ownership in other languages, but the borrow checker is teaching me how to be a lot more careful.
I have been doing compiler development in C++ for about 10 years, lisp before that and a bit of python more recently.
We could not figure out how to be productive in Rust after starting a greenfield project and sticking to it for a month.
Luckily this was not a project which requires incremental updates, we were on the verge of rewriting it in C++.
It took me almost a year to be as productive (and my code as canonical) in Rust as I was after over two decades of C++.
After a month of Rust I was still fighting the borrow checker and grokking basic concepts of the language. So no surprise in what you wrote.
However, after that year I am probably a factor of two to ten more productive in Rust.
The speedup is about two for everyday code (if it compiles it runs and doesn't crash). Ten (or more) when I need to bring in a dependency.
To qualify the latter: if a crate with same functionality or a Rust wrapper for a C/C++ lib exists, the factor can be much, much higher than ten since it is just adding a line to Cargo.toml.
If the dependency is a C/C++ lib it is as slow as using it from C++. It needs to be integrated with the build and that takes about the same time plus writing/generating a wrapper.
All that said: I had to write code in C++ the other day again. It almost felt painful after three years of only Rust.
It's one line of change for any build system..
Why?
Those ecosystems are built alongside C++ tooling, so no need to add an extra layer of indirection.
I already have the safety from those ecosytems, if I am reaching out to C++ is exactly because I need to do some unsafe stuff, or binding libraries that are anyway written in C++.
This is not a criticism of Rust, just a trait which needs to be acknowledged if the situation has to be improved.
I'm not sure if I agree with this. In fact, I'm pretty sure that I don't. These "occasional" nuisances become big blockers after a while and not only decrease productivity but hurt customer goodwill as well when they're not discovered in time.
I feel that it is actually harder to write bad code in Rust than it is in say, Python.
If you're really a startup, looking for a product-market fit, then you don't know exactly what you're building yet. There's a high change you're going to throw away a lot of code.
e.g. you spend effort caring about scalability, flexibility, proper architecture, full test coverage — only to hand it to users who will say they won't buy it, it's not actually the feature they wanted. You can learn such lessons with crappy copy-pasted code, and then worry about code quality once you've figured out what to actually build.
At the expense of safety.
So "there's a huge bifurcation between people who know this language and people who don't" is a good reason to pick another language. As with so many such techs, network effect can dominate other benefits.
Yeah, the infamous tin soldier approach that's often used to excuse using worse but more entrenched languages.
Rust or not, there's an argument to be made for statically typed languages improving productivity over the long run [0].
All development can feel sluggish depending on the work hours, estimates, business timelines, engineering skills, and the task at hand.
Programming languages are common targets because - we use them so much - there are so many - and all developers, at some point, must choose to dedicate their time to one over the others.
Finally, productivity itself is only one performance characteristic. To focus only on that (without even a good definition of effect or measure) makes content precisely what the author claims to avoid; flame bait.
[0] - https://www.researchgate.net/publication/338162224_A_Study_o...
Especially that is has first-class support for gRPC, runs on every cloud/on-prem host you can think of and doesn't force you to go out of your way to get most performance out of your implementation. People should not be mentioning Java first as a GC/JIT-based language for cloud given how competitive C# stack is and how much more you get straight out of the box.
I haven't used it in years, but I didn't find the anywhere near as full an ecosystem around C#, it's tooling, it's libraries, etc. It didn't help that there was a lot of confusion around what ran on which runtimes/versions.
Perhaps it's changed since then.
About 2 weeks in, I hit a blocker, where the most performance critical layer was running 30x slower than C#. It's two weeks since then, and I'm still blocked. Experienced programmers on the language Discord and Reddit have been stumped as well, and I get the impression this is what one should expect from an immature ecosystem, but also makes me fear that it will never actually catch on.
Point is, I think even the selling point of speed is inappropriate.
As for Rust’s performance, it has been proven time and again to approach that of C and C++. One counterexample does not disprove that.
In a single threaded version, it beats C#, though not by as much as I would have expected. The essence is that I have to run the same calculation on a large array of doubles that spits out another array of doubles, so I parallelize it with SIMD and threads. In C# I max out at AVX-2 for instruction level parallelism, but for Rust, I use AVX-512, and it's not even 2x faster, though it is faster--it should be more than 2x faster because AVX-512 has better instructions to work with. But when I combine this with doing the calculation in threaded parallel chunks on the array, it goes far slower than it should.
I'd argue C++ is not anywhere easier, if you are using its features or need to analyze the code that does. It won't be anywhere quick.
> I don’t know about anyone else, but at least for me, when I’m building a new feature I usually don’t have all the data types, APIs, and other fine details worked out up front. I’m often just farting out code trying to get some basic idea working and checking whether my assumptions about how things should work are more-or-less correct. Doing this in, say, Python is extremely easy, because you can play fast and loose with things like typing and not worry if certain code paths are broken while you rough out your idea. You can go back later and make it all tidy and fix all the type errors and write all the tests.
> In Rust, this kind of “draft coding” is very difficult, because the compiler can and will complain about every goddamn thing that does not pass type and lifetime checking — as it is explicitly designed to do. This makes perfect sense when you need to build your final, production-ready implementation, but absolutely sucks when you’re trying to cruft something together to test an idea or get a basic foundation in place. The unimplemented! macro is helpful to a point, but still requires that everything typechecks up and down the stack before you can even compile.
This rings so true for me. I could "mock up" entire apps using interfaces in Java, without having to actually write impl code. I could be sloppy as hell around the edges, but that didn't matter, because I could get the large design right without the compiler screaming.
In Rust, there is the chasm between no code and anything that works, feels so draggy.
> Over time, we grew the team considerably (increasing the engineering headcount by nearly 10x), and the size and complexity of the codebase grew considerably as well.
At this point, Rust is providing security and protection from technical debt. You have lots of new comers to the company/project and each one of them is adding or changing something. Developers being normal human beings have the tendency not to read documentation, old code or discuss with 100 engineers before building something.
Rust forces that on them, to a certain degree. Sure, you can move faster without Rust for now; but you'll pay the price later on. I am working on a company where we are doing microservices both in Rust and TypeScript. It's faster to get the job done in TypeScript; however, the cost is the maintenance later. The TS microservices breaks easily, regress much faster when someone modifies them, and are prone to invisible bugs. Rust is more solid in that front.
But in a world where speed of delivery and delivery itself (just deliver and deal with it later), Rust will definitively not shine. This is kicking the problem down to somebody else.
Another point: Because of that, Rust libraries are usually pretty solid. Comparing to the untangled mess in NPM, Rust will break down much less frequently.
Editor/IDE should be able to do this in a few seconds.
Those three things are actually just different facets of the same thing: ownership. The bad news is that you must learn Rust's ownership model to use Rust idiomatically. The good news is that you can do a lot without learning Rust ownership model at all. Just clone all your values. Not advisable for production code, but great for getting over the ownership model hump.
Context matters a lot, and you shouldn't be making tech choices based on the way the wind is blowing ("tech radars", what's hot in the blogosphere, etc.). If hiring teams to work on your mostly-CRUD app easily is a high priority, then Rust probably isn't a good choice. If you have a team that already knows Rust, and you need to add some web app / service, then Rust is a perfectly fine choice, on the grounds that support for web stuff is "good enough" now, and the best tool for the job is often the one you already know well and are already supporting.
If I'm building stuff for _myself_ and it's getting too fiddly for a bash script, then I'll always default to Rust just because _personally_ I'm way more productive in it than anything else.
Context, context, context.
If your alternative is Python or Ruby, then cloning your values is perfectly OK for production code. It will still run very fast.
By contrast, using Rust makes it easy to force a readable coding style on yourself and others.
You know what Rust looks like to me? Perl without the dollar signs. There's your write-only language, you just have it backwards.
But the article pretty clearly outlined a super simple CRUD app. These days that's a problem space so well defined you can practically specify the whole thing in command line flags. Rust really has little to add there. For other stuff, yeah Rust can be super cool.
Can you expand on this, please?
Whenever I work on the Python service I feel like I'm working in clay. Like, everything kind of sort of works. It won't at first, but then you just poke at it with a stick until it does. In Rust, it works or you're told exactly why it doesn't and then you fix it.
I change a struct and the compiler provides me with a list of places that need updating.
I have many reasons (mypy, type system, testing, venv, python 2), but really the big one is rust's superior type system as well as general tooling (lsp is way better, cargo and clippy are phenomenal).
On a big project, you are going to be reading a lot more code than you are going to be writing.
Just like how SQL gives you a faster way to not only store and search through binary trees but also to write the instructions to do so means that we should be able to do the same for web applications.
I’m not sure that most parts of a web application need to be Turing complete. User permissions? Data validation? Can’t we have GUI tools with a 1:1 mapping to more declarative code, ala SwiftUI? Why are we using a garbage collector for an HTTP request when we could just be freeing up memory after the response is sent?
Ur/Web: http://impredicative.com/ur/
Dark (Darklang): https://darklang.com/
Second, when you wire up a web server module with a SQLite module with a JSON module and translate between them in Ruby or Python you’re taking a really big performance hit. Just think about how many times something is being translated into an unused intermediate state because that just happens to be how the ready-made pipes are sized.
I’m saying, let’s build a bunch of ready-made pipes that are built specifically for SQL backed HTTP servers and with the features that we all seem to use on every project.
The end result should make it easier and faster to build a more performant web application.
But here's something to really chew on. Do we need the HTTP server at all?
Consider: why exactly do we need so much middleware plumbing for ordinary database driven web apps? A lot of this is to do with a winding evolutionary path rather than what you'd design with a clean slate.
Something I've been experimenting with lately is writing apps that connect directly to an RDBMS using its native protocol, over the internet, from a desktop app. Obvs the next step after is to try the same with a mobile app. Historically we've relied on web browsers and written translation layers for a few different reasons, but tech has been improving over time and some of those reasons are becoming obsolete:
1. Free relational databases were really rough back then and the commercial DBs often had weird limitations around distributing DB drivers. These days it's easier and postgres is a lot better.
2. You needed to slather lots of caches and other stuff in front of the RDBMS if you had high traffic. Modern DBs are a lot better at materialized views, cached query plans, read replicas etc. Postgres has a variety of load balancers that can sit in front of it and work around its per-connection overhead.
3. Security wasn't good enough. Nowadays you have features like row level security, security-definer views etc in the free DBs.
4. People translate everything to HTML/HTTP partly because it was too hard to distribute and update apps to Windows desktops/laptops in the 2000s, due to MS just generally losing the ability to execute and being distracted by stuff like Longhorn/WinFS. Browsers solved the basics of caching and refreshing of code for you. Now there's https://conveyor.hydraulic.dev/ which makes distributing desktop apps way easier. It's a lot more feasible now to just knock out a quick desktop app and upload to an S3 bucket, with built in online updates. Also the next release will support force-update-on-start, so you can get a web like experience where the user is always up to date so the schemas and client can be evolved in parallel.
If you could just write your UI using a 'real' UI toolkit and invoke directly to the DB from the UI code, you wouldn't need a lot of the microservices plumbing anymore, you wouldn't need to serialize stuff to JSON or HTML. You could just use the underlying DB protocol as an RPC layer and let the DB enforce business logic. It can be done for common kinds of enterprise apps today: there are only a few problems to solve, none of them especially hard. For instance a little service that turned OAuth SSO into client side certificates would make it easy to connect to a DB without needing to manage usernames or passwords. A tunnelling solution to get through awkward firewalls would also be useful (websockets etc).
For consumer apps there's more complexity. You wouldn't try to implement twitter that way for instance. But still, for the kind of SaaS-y thing that the article is about it could have saved a lot of complexity and improved iteration speed.
Funny enough, another thread introduced me to Truffle and I came across this article of yours!
This is a great point. I understand liking a language, but don't bring Rust into a GC space (in industry, personal projects can be what you like!) You can find a GC language with all the features of rust you like.
Rust forces that on them, to a certain degree. Sure, you can move faster without Rust for now; but you'll pay the price later on
If you're lucky enough to be paying any price later in a startup then your business is living.The last thing on my mind when commercializing something is future technical debt.
The flip side argument for an early stage company is that if they can’t move fast they may not survive. Not having tech debt is great, but it’s better to have tech debt and be alive than no tech debt and out of business.
To be clear, I’m not personally taking a hardline position on either side here. I think these kinds of choices are always a balancing act. Moving too fast can kill your business just as effectively as moving too slow: it’s just as hard to ship changes to a permanently on fire ball of spaghetti as it is a pristinely typed piece of clockwork, and sometimes harder.
True for most startups.
That's a significant difference.
It's the only difference if you're only considering blub exceptions, and not a nice exception system in which raising an exception doesn't immediately unwind, so that you can have remote handlers choosing local restart points and such.
> you need to change
Say I can't change it. I want to pass the exception through a third party library I don't control. I don't want to fork it, or can't.
Sum types with pattern matching are definitely nicer than some integer error codes or what have you, but for error handling, they are just a lipstick on error codes.
People outside of the Rust bubble do know what these concepts are, and about languages like OCaml.
Moreover, everyone who ever had anything to do with the concept of the development of exceptions was not an idiot who just didn't see the light of returning a sum type.
> you can handwave that away too
With what plugin for what editor ... :)
If you only work on <1000 line scripts, Python is a blast. If you work on projects with >1M LoC, it gets very hard to keep everything you need in your head if you want to understand code that other people have written.
- if it calls a function on `self`, where is it actually implemented?
- if you pass arguments, can they be mutated?
- can the function throw an exception?
you can't really answer this questions easily and I would argue answering them in Rust is much easier. Also because tooling for statically typed languages can derive much more than for dynamically typed languages.
Every time I've worked on—or seen other people work on—a "we must hurry at all costs project", enough time is wasted on debugging, firefighting and friction that just building a decently clean system from the get-go would have been faster. I've seen this unambiguously on "throw away" data science code meant to produce a single report (or normal science code meant for a single paper), and that's a much shorter-term effort than anything at a startup. The tradeoff is even clearer once you start working in weeks and months, much less quarters or years.
That is simply not true. Lots of build systems assume certain things in certain places and output to be something (what if you want a .so but the build only spits out .a etc.). In any case you may have to resort to shell scrips or launching shell scrips from your own build system. In short: it sucks.
And there is also the case when the dependency has dependencies itself. Adding a dependency is one of the biggest PITAs in any C/C++ project. For these reasons:
1. No standardized build system.
2. No standardized use/configuration of common build systems (everyone uses CMake/SCons/whatever slightly differently)
3. Dependencies may have other dependencies, which use yet another build system (see 1. & 2.)
This is the reason why 'single header library' is a 'feature' C/C++ devs look out for when deciding on taking on a dependency.
It makes adding such 'libraries' as deps a no brainer as far as the above is concerned. I.e. your average Rust crate is to your Rust project as is a single header C/C++ to your C/C++ project. Every other kind of dependency is potential pain.
That Rust can check these somewhat more explicitly (rather than via good coding style) and that C++ also allows you to do arbitrary permutations (a la non-const references) is what you’re talking about. But ownership is very very real in C++! Just look at the craziness that is move semantics!
These have very different semantics. Lexically you can only have either 1 mutable reference or N immutable references at a time to a given object. This is the foundation for a lot of the safety and aliasing [2] guarantees. Just because they both use an '&' doesn't make them equivalent! :)
Don't get my started on `std::move` which doesn't really move, and continues to allow you to use the source object - in whatever condition it may be in. These are also not the same. C++ move semantics are sort of the 'ruined fresco' [1] of Rust move semantics.
[1] https://www.npr.org/sections/thetwo-way/2012/09/20/161466361...
Like a lot of C++ things there's a big theme of "can we have X? we have X at home. X at home: o_O"
They also have an advantage over cargo, specially Conan, binary repos, no need to recompile the world for 3rd party dependencies after each git clone.
let x = match foo() {
Ok(value) -> value,
Err(e) -> return Err(e),
} let x = match foo() {
Ok(value) -> value,
Err(e) -> return Err(e.into()),
}
Which can automatically convert one error type into another when the appropriate From and/or Into impls exist.https://github.com/rust-lang/rust/blob/4e0d0d757e2f1b61ec809...
You might be saturating your memory bandwidth to the point where it just can't go any faster. Since it seems your problem is easy to parallelize, you might want to experiment with the rust-gpu ecosystem.
Rust's default paradigm for memory management seems more significant of a language feature in my opinion and is what I can imagine most people don't like. A lot of the programs I write don't fall into the class of problems Rust is trying to prevent which makes the restrictions it enforces bothersome for me.
And rust has the `?` operator, as well as combinators like `map`, `and_then`, `unwrap_or_else`, etc. which IMO make the flow much easier to follow than say `if option.is_none() { return None }`.
> Rust's default paradigm for memory management seems more significant of a language feature in my opinion and is what I can imagine most people don't like
I never said it wasn't. My point is that if you use a language enough you get used to things that are difficult for people less experienced with that language.
People complain about Go's wordy error handling, but systems programming is error programming. The places where Go's error handling is most annoying is in application code, where you'd ordinarily EAFP instead of LBYL. But that's also the code Rust is least convenient for.
Go vs. Rust is truly the dumbest programming language slapfight in the entire industry.
If you forget err != nil, well your value does actually have a value and you would think your result was 0.
As long as there is proper linting, both should be readable.
That being said, python is about the simplest language there is to read.
Of course if you have many times nested and improperly indented list comprehensions- sure that gets confusing. But that should be fixed with proper linting (black on its own should do it)
Curious about the examples you've seen that were difficult to parse!
foo = x if y else zMany languages support the ternary operator, where it would be:
foo = y ? x : z
Which "feels better" but I think that's because I learned ternaries first.Some languages like Rust and Kotlin do support assignment of an if statement like
foo = if (y) { x } else { z }
And I think that's a good step, as it doesn't need to introduce new syntax, just allows assignment of "blocks"[citation needed]
> 2. Faster development that's easier to change on a dime.
Yes, development can be faster at the expense of safety, which is a trade-off that favors the use of other languages at early company stages. Agreed here.
> 3. No risk of memory leaks (which can happen in safe rust)
... at the expense of making collection non-deterministic and significantly more resource intensive. Not to mention that the non-determinism means you generally end up grafting your own resource management scheme on top. I've never seen a company that hasn't at some point had someone crying over GC tuning. Leaks are safe (which is why they're allowed in safe Rust) and generally speaking both a non-issue - and very easy to track down.
Rust has been my daily driver at home since like 2015, and I have to say, I've never once gone "man you know what this thing needs? Shenandoah." I write a bunch of Kotlin too, for what it's worth.
These trade-offs are well known and there are pros and cons to both.
2. No. It is not at the expense of safety. Again, Rust allows you to do unsafe things using the “unsafe” keyword. GC languages simply will not allow it.
3. Strong reference counting memory leaks is not easy to track down. Many embedded systems have been taken down by them.
That's not what you originally claimed though. You made some claims about 'risk of future unsafety' and I'm not sure what that means?
For the second half of your claim, Rust's safety guarantees extend well past memory safety - and a collector doesn't guarantee memory safety at all. Yes, most GC'd languages are memory safe too. However, you can just strap the Boehm GC to C or C++ [2] and that doesn't suddenly, magically, make it memory safe.
> 2. No. It is not at the expense of safety. Again, Rust allows you to do unsafe things using the “unsafe” keyword. GC languages simply will not allow it.
Safety means a lot of different things. GC isn't a replacement for safe Rust. Rust offers many kinds of safety, memory safety is just one. And yeah, unsafe is a tool for implementing certain things that cannot be expressed in safe Rust, but it's extremely rare that you would dip into it in production code. It's more for library authors who wrap unsafe APIs in safe ones.
If you're curious what 'safe' rust really means and why the GC doesn't subsume all its features - and the difference between safe and unsafe Rust - I'd recommend [1].
> 3. Strong reference counting memory leaks is not easy to track down. Many embedded systems have been taken down by them.
Sure in embedded it can be hard, but on a PC you can just use valgrind or the leaks tool. If you're writing embedded code in Rust just don't use Rc or Arc boxes and you can't form a cycle. Remember there's no reference counting in Rust at runtime unless you opt into it with a shared-ownership reference-counting box. All Rust's reference counting happens in the compiler and once the compiler determines your object is no longer referenced, it drops it. If it cannot make that determination statically your build will fail.
Reference counting is generally how resources are managed in GC'd languages since you're explicitly not to rely on the finalizer ever being called. Files, sockets, etc. It's just trading off one kind of problem for another.
[edit] Note that you can also leak memory in a GC'd language by keeping a reference to objects you no longer need - for instance, in some long-lived dictionary or array, or in a static. [3]
[1] https://doc.rust-lang.org/nomicon/meet-safe-and-unsafe.html
[3] https://www.lucidchart.com/techblog/2017/10/30/the-dangers-o...
In the beginning of doing Rust I was missing the ternary operator, but now I couldn't care less.
foo = if y { x } else { y }
Works well :-)Every Go project I've worked on has used this linter. I think it should be builtin, but it's very easy to incorporate.
As someone who is adding this to a Go project with hundreds of unchecked errors, I disagree.
Anecdotally this is the same argument many C++ devs make in regards to Rust safety guarantees. The difference is just...different.
Also to point out, you can 100% leak memory in safe rust
"Can" is the whole thing. A rust dev risking can is like a C++ dev risking can.
I like rust and do what you want in personal projects, but introducing it to a GC space when there are langauges that "can't" be unsafe is irresponsible when a GC language can offer all the upside of rust.
GCs prevent memory leaks, which safe rust can do. So even if you somehow make sure no unsafe is ever used, you can still introduce a hard to debug issue just because you want to use rust.
It's also worth pointing out that most GC languages can have the same issues as a Rust program (if not more) just by virtue of having FFI. In Python, I can quite happily load a C extension and ignore all the rules. Whereas in Rust, if I want to use a C library, I still need to explicitly declare it as unsafe code.
In practice, I tend to find that I have to think more about how I approach parallel/multi-threaded code in Rust than I do in other languages - not because it's dangerous, but because the compiler doesn't allow code that would break memory safety guarantees. In some ways, that's obviously a bad thing if I'm having to think more, but in my experience, the thoughts often lead to a better design anyway. So in the end, I'm more confident in my Rust code than I am in equivalent code in a GC language.
Many memory safety violations have been discovered in rust crates: https://www.infoq.com/news/2021/11/rudra-rust-safety/
Maybe I wrong on that, but if it can’t guarantee crate safety, then it doesn’t really do anything.
They're not the same because how C++ does memory safety and how Rust does it is unequivocal. C++ is unsafe by default, Rust is not, and you are ensured it is not, as I mentioned, via things like the borrow checker. To equivocate them is to fall into the same trap you yourself mention.
There are no GC vs non-GC spaces. The point of Rust is to be able to have memory safety without a garbage collector, that's literally why it was made. Thus, it is expected based on that premise that we be able to use Rust wherever a GC could be used. The fact that unsafe exists does matter, as long as unsafe isn't used. I too can turn a GC language like Java into an unsafe monstrosity, by for example doing raw bytecode tinkering, does that mean that Java is now unsafe? No, unsafe is merely an escape hatch.
Rust comes with additional risk. It is easier to leak memory, it is easier to be unsafe (especially since you can't guarantee what future other devs will do), but you gain nothing.
You get all that risk, but fearless concurrency can be done in GC languages (like Elixir) and many people have created the Result type before. So you have added risk for no benefit. Not to mention its easier to hire devs (and train devs) for other GC languages cause not everyone knows or understands the borrow checker.
Is there any advantage?
enum Foo {
A(String),
B(i32),
}
And it's.. not simple. And even if you manage to do it, it'll never be how Kotlin was meant to be written. sealed interface Foo
class A(val s: String): Foo
class B(val b: Int): Foo enum Foo {
Bar(String),
Baz(i32),
}
Why? It's such a fundamental thing to be able to say "this piece of data is either this or that.. and then have the compiler tell you if you missed a case.Ada is on my list of languages to look at. I'm cautiously optimistic about that one. But would you pick that over Rust as the simpler alternative? "Look guys! We're not moving fast enough with Rust because nobody seems to be proficient in it. Let's go with Ada instead!" .. I jest, but I will check it out and I really hope it hits the sweet spot for me
But even then, what about Ada?
I’m also not an expert, but I assume between C#/Typescript/haskell/swift that you can find all those things in many GC/safer languages.
I’m not saying a GC guarantees all safety, I am saying you can find a GC language that gives all the safety (and technically more) of rust.
My quote of “future unsafety” is that even if your code currently uses only safe rust, their will always be the chance someone adds unsafe rust, increasing the safety issues.
It just seems you can find a GC alternative that will never run into the issues rust can cause, and it will be faster to develope/change (because no borrow checker), and there is no downside.
Giving "all the safety" is theoretically possible, but "technically more" is not. And even if you did have "all the safety", you'd still have the big performance disadvantage with a GC language.
> My quote of “future unsafety” is that even if your code currently uses only safe rust, their will always be the chance someone adds unsafe rust, increasing the safety issues.
No there won't. This is what #![forbid(unsafe_code)] is for.
The value of something like "unsafe" is not that your code is magically protected if it isn't there, but rather that it provides a warning sign as to where dangerous code might lie. So if something goes go wrong - or you're just worried something _might_ go wrong - you know roughly where to look. In the same way, if your Python code includes a C extension and you start getting weird segfaults, you can reasonably guess where to start looking.
Except that, because unsafe is so well integrated in the language, you can often significantly reduce the impact of unsafe to only a few lines, and in most cases you won't need to use it at all.
foo := Foo{"hello"}
fooPointer := &foo
fooPointer = nil
fmt.Println(fooPointer.Bar)
> panic: runtime error: invalid memory address or nil pointer dereference
In a language like Kotlin a compile time error would prevent `fooPointer.Bar`, and in Rust `nil` does not exist. Go also does not have sum types, see for instance https://making.pusher.com/alternatives-to-sum-types-in-go/The rest all are missing basic things. Like, I love TS, but it's absolutely bonkers because js is js. I once worked on a 250k loc project of js/ts, and we had nothing but trouble
My point is you can find a safe GC language with all the benefits of rust.
There were over 200 memory safety violations discovered in rust crates: https://www.infoq.com/news/2021/11/rudra-rust-safety/
Elixir for example, is very safe and guarantees more safety than rust.
I'm not sure how they could be, given that Rust checks exhaustively and Go doesn't. If one programmer messes up, then that's it, an unhandled exception might occur in the future. I would rather rely on the computer telling me when it should be handled over humans.
I also don't use much Go because it lacks good algebraic data type support, honestly can't imagine going back to a language that doesn't have them as I like to define all my business logic in types and then build the actual application from there. Go simply doesn't have that capability.
The language maintainers don't want to add it: https://github.com/golang/go/issues/19412
Speed is the only one you may possibly not, but I think speed is an non-issue. Speed in programming never matters except in the systems space. No one is gonna be able to tell Rust vs Go in a web API and depending on how you do the benchmark, I think many GC languages can probably tie Rust.
This kind of attitude is how we get bloated Electron apps that HN (including me) loves to complain about. Speed should be a first class consideration, not something to throw away for later.
> No one is gonna be able to tell Rust vs Go in a web API
Debatable, Discord moved from Go to Rust [0] because they were getting latency and CPU spikes from Go's GC, and I'd assume that's the same with any GC language because, well, the GC has to run sometime. Now you might say that we're not all Discord scale, to which I'd say, like above, there are benefits to Rust that are more than just speed. Cargo alone is nigh unbeatable compared to some other languages. I was trying to get Python to work the other day and was pulling my hair out over venv, virtualenv, pip, conda etc.
It matters to whoever is paying for compute. If you get some other benefit, it may be a worthwhile tradeoff, but it always matters.
Speed equals money. The same app running at twice the speed uses half the compute resources. You may also gain a lot of simplicity if you don't need to distribute your architecture so early on.
I'm not convinced about this at all. I've had far more issues with memory leaks in JavaScript programs than I have with Rust. The only way you're really likely to leak memory in Rust is creating cycles of Rc and Arc types. But I don't think any of my programs have even contained Rc, and only trivial usages of Arc for global resources, which I absolutely wouldn't want to point to each other. JavaScript will let you create the same problem without making it obvious with type signatures. And while you might be lucky and have the GC sort out your mess, it doesn't always manage to.
And the discord instance was because it was systems/performance critical software they rewrote. They didn’t rewrite there web APIs. Discord mentioned the rewrite is now rust with C++ glueing it together. No way you are using C++ unless you have to.
I’ll agree with you on python venv/pip. Ugh what a mess. I like cargo, but honestly I think you could argue cargo also is an issue. When you bring in 100 crates, you just gotta hope all the unsafe code in them are safe.
A static analyzer found 200 memory unsafety issues in rust crates: https://www.infoq.com/news/2021/11/rudra-rust-safety/
Memory safe GC languages you don’t have to worry about that, and you can get a package manager.
So bam, you just introduced memory safety issues into your web API. This is like a C++ dev using C++ and causing memory issues unnecessarily just because they don’t want to use a safe language.
And Elixir has concurrency and memory safety and it’s guaranteed, so no package you import can violate it. Unlike rust, where you can use safe rust and think there’s no violation, but then use a crate and suddenly have memory unsafety and concurrency violations.
These crates worries are wayyy better than normal C++, but they are terrible compared to any actually safe language.
And FFI? As in the Unix extensions? That’s the operating systems calls, not the language. By that logic, python is as dangerous as C++.
It’s crazy how so many people on this thread say rust is the only choice because of memory safety, and are now bending over backwards trying to claim memory safety is no big deal. So much so FFI is now being brought up smh.