my name is Bruno and I'm working with a small startup from Switzerland. We are in the process of finishing our MVP that we have written mostly in Python (some Cython and C++) and we are currently trying to find out what programming language we want to use in the future to create a production version of the before mentioned MVP. After some suggestions from friends and colleagues, I started looking into Rust as an alternative to C++ and I really liked what I found in my initial research.
My question now is if there are maybe some people here that are more experienced with the whole Rust ecosystem that could tell me if Rust already offers all the features that we would need. I will also include the results of my initial research (please correct me if I'm wrong in somethings).
The requirements that we have are:
It needs to run on an ARM based system -> Should be no problem for Rust.
We need an equivalent of Numpy for all kinds of matrix and vector operations, which is equally well optimized and easy to use -> ndarray could be an alternative but I'm not sure how mature it is.
We something to handle image processing and manipulation (basically OpenCV) -> I saw that there are OpenCV bindings for Rust but are they production ready?
We need to implement a multi-processing system with all the requirements to it (communication, error handling, etc.) -> I saw rayon. Is that the right framework for this task?
We need the option to communicate with other devices via Bluetooth.
Finally how is easy is it to create C++/Python "plugins" for parts where Rust is not ready yet and what is the general as well as performance impact of that?
If you have some ideas, opinions or advice that could help us I would greatly appreciate if you leave a comment.
rayon excels at parallel tasks with a definite beginning and end, where one thread says essentially "call this function on many threads and gather the results". If you want a more "streaming" style of execution where the parallelism never "finishes", you will probably need other tools (take a look at crossbeam, which is largely what rayon is built on). However, that's not to say it will be a mess; because Rust defines thread-safety concepts in the language and standard library, you can freely mix different tools. My own project uses a mix of rayon, plain threads, and async executors depending on the shape of each problem.
The ndarray crate has been around for quite a while and a lot of numeric crates build on top of it. It's a pretty reliable foundation to build on top of.
I wouldn't worry too much about the 0.15.4 version number - open source projects have a tendency to stay on 0.x versions for ages even though their API is stable and they are used in the real world. This is mainly a psychological thing and not an indicator of a project's maturity (i.e. going 1.0 implies a level of support/maintenance and that you won't do API breaks any more). There's even a satirical website about this phenomenon - About — zer0ver.
This is super easy!
For Python, check out the pyo3 crate. It's a library which binds to the Python C API and provides a lot of conveniences for declaring native Python modules. You may want to skim through the user guide to get a better feel for how it works.
Oh, and you can use numpy in Rust thanks to the pyo3. That'll help your company with the transition because it means users can pass a numpy array into your functions.
For the C++ side of things, the cxx crate is your friend. C++ interop is traditionally quite difficult because C++ doesn't really have a stable ABI, which forces you to go through a C interface. The cxx crate lets you declare the interface between your Rust and C++, then it'll do some code generation on both sides to make everything feel natural. The project also has a nice user guide that you might want to skim through.
I've personally used pyo3 for work and really liked it. I've dabbled with cxx, but a lot of the FFI work I do is with C APIs so I haven't used it in production.
The performance impact should be minimal because both solutions work by passing pointers to objects around instead of serializing the data. I'd just make sure to keep the API high level and operating on bulk data because FFI naturally adds a bit of overhead (e.g. if C++ calls an external function the optimiser can't perform inlining, or there might be a bit of context switching when jumping from Python to Rust and back again).
It's likely that most of our code runs on remote ARM based systems. We have not had any problems there.
Meanwhile, server side, we have a lot of fiddling around with decoding proprietary binary protocols. The likes of Python and node.js cannot hack it. It's needs the performance of a compiled language. We went for Rust, it works fine and performs as expected.
I'm not so clear on what you mean by "multi-processing" there. Even a web server in Rust using Rocket or other web framework can do that well in Rust. Rayon works well for parallelising other algorithms.
I have no experience of using OpenCV or Bluetooth from Rust. I suggest you try it. If it works it works.
Just now I really don't want to go back to the wibbly-wobbly world of C++, Python or JS.
Can you specify what you mean with "multi-processing system"? Does that mean multiple processes, multiple threads, the ability to serve requests concurrently? Depending on the answer, even tapping in the async ecosystem could make sense.
I might have been a bit flimsy with the description here. What I mean by "multi-processing system" is that we currently acquire images from our camera system and then ran two types of analysis in parallel. Additionally, we have for example a Bluetooth "process" that handles the custom communication with other devices that also runs in parallel.
For such things, you will likely not need anything more than the thread support in the standard library (though other libraries may offer more convenient tools to use, depending on your precise data/control flow patterns).
This sound like "a complicated custom system, not your typical web-service", and I am fairly certain that overall Rust is massively more productive for such systems than C++ (based on my experience with rust-analyzer and nearcore, two big custom systems I've worked with).
One risk here is that Rust rather strongly favors certain kind of architectures. There's Rust-specific quirks related to, eg, ownership or async, which inform the architecture of the whole system. So, if Rust is new for the team, there's a risk that the team will charge ahead implementing something which couldn't work well with Rust, and, eg, end up in molasses of arcs and mutexes.
So, I would advice to maybe try to front-load the "learning about Rust ways of doing things", to make sure that, when the team starts architecture things, it has somewhat deep Rust expertise.
To clarify, I don't think that Rust is going to be hard to write "locally" -- it's pretty productive language once you get past the initial bump. What I would worry about is global complexity -- things like picking library X as a foundation because it seemed great at the first blush, but, with experience, realizing that Y would have been a better choice.
thanks for the very helpful answer. You confirm my initial thoughts in this direction. Question would be if you know of some resources/tools/frameworks/books that you would recommend that would allow us to avoid these problems?
After you've mastered the basics of the language I would recommend checking out some of the material by Jon Gjengset. He has a YouTube channel and recently released Rust for Rustaceans which should help you on your path from competency to mastery.
You should also sign up for This Week in Rust. It's a newsletter about Rust and checking out the different articles that are published every week really helps you widen your understanding of the language.
I think the best thing is just practice: spend couple of weeks implementing a prototype which contains overall flow of data, but doesn’t actually do anything. From the sound of it, you probably want:
some kind of “game loop” to manage evolution of system’s state over time
some kind of thread-pool for compute-heavy workloads
some kind of (maybe) async networking (http/udp/Bluetooth) manage connection to peer devices
Prototyping all that without actual logic of data transformation should give you a good sense of what’s work.
A good litmus test for understanding Rust mojo is this post: dtolnay::_02__reference_types - Rust. I think as long as you have at least one person on the team for whom the introduced model is already natural, you should be fin
You might find the following collection of my posts useful as well: