This is a miscommunication between the values of “shipping” which optimizes for fastest time to delivery and “correctness” which optimizes for the quality of the code.
Rust makes it easy to write correct software quickly, but it’s slower for writing incorrect software that still works for an MVP. You can get away with writing incorrect concurrent programs in other languages… for a while. And sometimes that’s what business requires.
I actually wish “rewrite in Rust” was a more significant target in the Rust space. Acknowledging that while Rust is not great for prototyping, the correctness/performance advantages it provides justifies a rewrite for the long-term maintenance of software—provided that the tools exist to ease that migration.
Lately rust is my primary language, and I couldn't agree more with this.
I've taken to using typescript for prototyping - since its fast (enough), and its trivial to run both on the server (via bun) or in a browser. The type system is similar enough to rust that swapping back and forth is pretty easy. And there's a great package ecosystem.
I'll get something working, iterate on the design, maybe go through a few rewrites and when I'm happy enough with the network protocol / UI / data layout, pull out rust, port everything across and optimize.
Its easier than you think to port code like this. Our intuition is all messed up when it comes to moving code between languages because we look at a big project and think of how long it took to write that in the first place. But rewriting code from imperative language A to B is a relatively mechanical process. Its much faster than you think. I'm surprised it doesn't happen more often.
I'm in a similar place, but my stack is Python->Go
With Python I can easily iterate on solutions, observe them as they change, use the REPL to debug things and in general just write bad code just to get it working. I do try to add type annotations etc and not go full "yolo Javascript everything is an object" -style :)
But in the end running Python code on someone else's computer is a pain in the ass, so when I'm done I usually use an LLM to rewrite the whole thing in Go, which in most cases gives me a nice speedup and more importantly I get a single executable I can just copy around and run.
In a few cases the solution requires a Python library that doesn't have a Go equivalent I just stick with the Python one and shove it in a container or something for distribution.
I'm in the camp of "If your target is Go, then prototype in Go." I don't bother with the intermediate step. Go is already so very close to being a dynamic language that I don't get the point. Just write "bad" Go to prototype quickly. Skip the error checks. Panic for fun. Write long functions. Make giant structs. Don't worry about memory.
You mentioned running someone else's python is painful, and it most certainly is. No other language have I dealt with more of the "Well, it works on my machine" excuse, after being passed done the world's worst code from a "data scientist". Then the "well, use virtual environments"... Oh, you didn't provide that. What version are you using? What libraries did you manually copy into your project? I abhor the language/runtime. Since most of us don't work in isolation, I find the intermediate prototype in another language for Go a waste of time and resources.
Now... I do support an argument for "we prototype in X because we do not run X in production". That means that prototype code will not be part of our releases. Let someone iterate quickly in a sandbox, but they can't copy/paste that stuff into the main product.
Just a stupid rant. Sorry. I'm unemployed. Career is dead. So, I shouldn't even hit "reply"... but I will.
I second your experience with Python. I've been coding in Python for 10+ years. When I get passed down some 'data scientist' code, I often it breaks.
With Rust, it was amazing - it was a pain to get it compiled and get past the restrictions (coming from a Python coder) - the code just ran without a hitch, and it was fast, never even tried to optimize it.
As a Python 'old-timer' , I also am not impressed with all the gratuitous fake typing , and especially Pydantic. Pydantic feels so un-pythonic, they're trying to make it like Go or Rust, but its falling flat, at least for me.
Is there a good resource on how to get better at python prototyping?
The typing system makes it somewhat slow for me and I am faster prototyping in Go then in Python, despite that I am writing more Python code. And yes I use type annotations everywhere, ideally even using pydantic.
I tend to use it a lot for data analytics and exploration but I do this now in nushell which holds up very well for this kind of tasks.
When I'm receiving some random JSON from an API, it's so much easier to drop into a Python REPL and just wander around the structure and figure out what's where. I don't need to have a defined struct with annotations for the data to parse it like in Go.
In the first phase I don't bother with any linters or type annotations, I just need the skeleton of something that works end to end. A proof of concept if you will.
Then it's just iterating with Python, figuring out what comes in and what goes out and finalising the format.
> Rust makes it easy to write correct software quickly, but it’s slower for writing incorrect software that still works for an MVP.
I don't find that to be the case. It may be slower for a month or two while you learn how to work with the borrow checker, but after the adjustment period, the ideas flow just as quickly as any other language.
Additionally, being able to tell at a glance what sort of data functions require and return saves a ton of reading and thinking about libraries and even code I wrote myself last week. And the benefits of Cargo in quickly building complex projects cannot be overstated.
All that considered, I find Rust to be quite a bit faster to write software in than C++, which is probably it's closest competitor in terms of capabilities. This can be seen at a macro scale in how quickly the Rust library ecosystem has grown.
I disagree. I've been writing heavy Rust for 5 years, and there are many tasks for which what you say is true. The problem is Rust is a low level language, so there is often ceremony you have to go through, even if it doesn't give you value. Simple lifetimes aren't too bad, but between that and trait bounds on some one else traits that have 6 or 7 associated types, it can get hairy FAST. Then consider a design that would normally have self referential structs, or uses heavy async with pinning, async cancellation, etc. etc.
I do agree that OFTEN you can get good velocity, but there IS a cost to any large scale program written in Rust. I think it is worth it (at least for me, on my personal time), but I can see where a business might find differently for many types of programs.
> The problem is Rust is a low level language so there is often ceremony you have to go through, even if it doesn't give you value.
As is C++ which I compared it to, where there is even more boilerplate for similar tasks. I spent so much time working with C++ just integrating disparate build systems in languages like Make and CMake which just evaporates to nothing in Rust. And that's before I even get to writing my code.
> I do agree that OFTEN you can get good velocity, but there IS a cost to any large scale program written in Rust.
I'm not saying there's no cost. I'm saying that in my experience (about 4 years into writing decently sized Rust projects now, 20+ years with C/C++) the cost is lower than C++. C++ is one of the worst offenders in this regard, as just about any other language is easier and faster to write software in, but also less capable for odd situations like embedded, so that's not a very high bar. The magical part is that Rust seems just as capable as C++ with a somewhat lower cost than C++. I find that cost with Rust often approaches languages like Python when I can just import a library and go. But Python doesn't let me dip down to the lower level when I need to, whereas C++ and Rust do. Of the languages which let me do that, Rust is faster for me to work in, no contest.
So it seems like we agree. Rust often approaches the productivity of other languages (and I'd say surpasses some), but doesn't hide the complexity from you when you need to deal with it.
> I don't find that to be the case. It may be slower for a month or two while you learn how to work with the borrow checker, but after the adjustment period, the ideas flow just as quickly as any other language.
I was responding to "as any other language". Compared to C++, yes, I can see how iteration would faster. Compared to C#/Go/Python/etc., no, Rust is a bit slower to iterate for some things due to need to provide low level details sometimes.
> Rust is a bit slower to iterate for some things due to need to provide low level details sometimes.
Sometimes specific tasks in Rust require a little extra effort - like interacting with the file picker from WASM required me to write an async function. In embedded sometimes I need to specify an allocator or executor. Sometimes I need to wrap state that's used throughout the app in an Arc(Mutex()) or the like. But I find that there are things like that in all languages around the edges. Sometimes when I'm working in Python I have to dip into C/C++ to address an issue in a library linked by the runtime. Rust has never forced me to use a different language to get a task done.
I don't find the need to specify types to be a particular burden. If anything it speeds up my development by making it clearer throughout the code what I'm operating on. The only unsafe I've ever had to write was for interacting with a GL shader, and for binding to a C library, just the sort of thing it's meant for, and not really possible in those other languages without turning to C/C++. I've always managed to use existing datastructures or composites thereof, so that helps. But that's all you get in languages like C#/Go/Python/etc. as well.
The big change for me was just learning how to think about and structure my code around data lifetimes, and then I got the wonderful experience other folks talk about where as soon as the code compiles I'm about 95% certain it works in the way I expect it to. And the compiler helps me to get there.
There is a real argument to be made that quick prototyping in Rust is unintuitive compared to other languages, however it's definitely possible and does not even impact iteration speed all that much: the only cost is some extra boilerplate, without even needing to get into `unsafe` code. You don't get the out-of-the-box general tracing GC that you have in languages like Golang, Java/C# or ECMAScript, or the bignum-by-default arithmetic of Python, but pretty much every other basic facility is there, including dynamic variables (the `Any` trait).
In an ideal world, where computing software falls under the same liability laws as everything else, there is no shipping without correctness.
Unfortunately too many people accept using computers requires using broken produts, something that most people would return on the same day with other kind of goods.
> Rust makes it easy to write correct software quickly, but it’s slower for writing incorrect software that still works for an MVP
YMMV on that, but IMHO the bigger part of that is the ecosystem , especially for back-end. And by that metric, you should never use anything else than JS for prototyping.
Go will also be faster than Rust to prototype backend stuff with because most of what you need is in the standard library. But not by a large margin and you'll lose that benefit by the time you get to production.
I think most people vastly overestimate the friction added by the borrow checker once you get up to speed.
Rust makes it easy to write correct software quickly, but it’s slower for writing incorrect software that still works for an MVP. You can get away with writing incorrect concurrent programs in other languages… for a while. And sometimes that’s what business requires.
I actually wish “rewrite in Rust” was a more significant target in the Rust space. Acknowledging that while Rust is not great for prototyping, the correctness/performance advantages it provides justifies a rewrite for the long-term maintenance of software—provided that the tools exist to ease that migration.