This doesn't match my experience. It took some time to get up to speed, but at this point it's much faster for me to write something in Rust than in other languages that I'm proficient in that are ostensibly faster to develop in (e.g., JavaScript).
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.
FWIW the “parse don’t validate” attitude which has become popular in TypeScript circles does this equally well. In Rust you’ll define the types you expect to deserialize as structs with Deserialize trait implementations, in TypeScript you’ll define a basic schema with runtime decoding and corresponding (inferred) static types. Like you say, maybe 45 seconds more typing, with the same value guarantees.
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.
To me the greatest thing about TypeScript is that there are times that I absolutely know that something doesn't need to be extra safe or to have validation, and I can easily slap a `// @ts-expect-error` on it and either ignore it or come back and fix it later. I know some people think that's a horrible thing and sacrilege, but damn it if it makes it easy to move fast when you need to.
Won’t speak for GP, but I’ll say why I think @ts-expect-error is a useful tool for this. It’s not so much a time saver as a time rearranger. Assuming a few conditions:
- 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.
I take your comment to be a jab at JS, which I’m not a particular fan of, but I don’t think it holds much water: the GP’s example is nearly identical in Ruby and Python as well, and would have the same problem in those languages.
Another interpretation would be that Rust and JS cannot possibly cover all or even most programming domains, so there must be other languages out there that need to be in the ring for any given application.
if you also wanted to pull in a node framework for deserilization you could import something like `zod` to have the similar level of nice error messages when invalid input is encountered.
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).
Feel free to weigh my data point as you see fit or ignore it altogether. It differs from the OP’s (which is why I thought it worth mentioning) and their experience is just as valid. To answer your question, I’ve spent the last three years at a startup building with Rust. Before that I spent 9 years at a startup building on Node.js.
Ok, for some perspective here, that 45 seconds longer is about 50-100 times how long it took to do the one liner in javascript. Extrapolate that to generally 50-100x development time for the development of an entire project and you're looking at weeks to months longer development time to solve the same problem. Rust is slower to develop in by a significant factor than js, python, ruby etc.
What I was trying to get at with the (silly) example is that the time-to-first-execution of a working program on good input might be much faster with something like JS, but the time to have a debugged version that fails gracefully (and clearly) on operational errors may not be. It’s okay to disagree, and it’s also okay to agree and still feel Rust makes the wrong tradeoffs. I just mention it because I think people sometimes overweight the time-to-first-run.
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.