4 ms·
I too program in rust, however I don't like programming in rust. It feels like C++ on steroids. I consider all the meta-programming and traits mostly read only.
by Cloudef 4y ago
I too program in rust, however I don't like programming in rust. It feels like C++ on steroids. I consider all the meta-programming and traits mostly read only. There's too many concepts you have to understand and wrappers on top of wrappers. And even when you know what you want to do, it's sometimes a huge fight to get rust to understand what you are doing. Even with unsafe this process can be really annoying and frustrating. Compile times are slow and binaries big.
For example, recent struggle with rust I had. Was storing something pre-computed into a global variable (in C terms think of pointer to binary data). And after it's stored this variable is never mutated anymore, just read, so it's completely safe. Unfortunately it felt almost impossible to get rust to understand this, and it always wanted to copy the damn thing, or preaching me how it's unsafe.
That said, rust is still very much good tool to have in pocket when you have to program high-performance and secure software. But I would not ever program a huge project with it, perhaps use it only in critical parts.
- allisdust 4y agoFor your issue there are two ways to achieve it: 1) Use lazy_static crate 2) Use a arc wrapped rwlock
- Cloudef 4y agoNeither worked, it always complained about Copy trait, and no I don't need a mutex for this. I eventually got it to work with few bits of unsafe, but then I gave up when one of the apis I was using captures mutable self, and after that I couldn't force rust to move the damn thing. I decided to not cache anything at all in the end. But it just shows how some trivial things can be nigh impossible in rust, or less efficient. (Also I had to wrap the thing I was caching into yet another struct just to give it a dummy Send and Sync trait)
- allisdust 4y agoIt's a RWLock. Not a mutex. As long as no one is writing to it, readers are not blocked.
- Cloudef 4y agoI know, but the RWLock itself also did not work. Complaining about safety and the only solution seemed to wrap the value itself into mutex inside rwlock. If I still had to go back to this, I would just write the function in C and go from there. Rust is productive when you can work with the highest level of abstraction and when it can copy values freely. When you want to work with copy-free or shared memory, is when rust becomes special kind of hell, and you have to dwelve deep into the depths of rust to get it to do what you want. Somehow the meta-programming is even more unreadable than C++. I also get slight PTSD whenever I have to write `new`, not sure if the method will allocate or not behind the scenes. This is one of the things zig gets right I think: - No hidden control flow. - No hidden memory allocations.
- allisdust 4y agoMay I suggest you take some time to understand the concurrency and memory management parts of Rust a little more to figure out where your program is falling. I agree it's not an easy language to pickup, but the use case you mentioned is a pretty common one and there are several ways to solve it. For example, any kind of config reading from a file needs it. Similarly, connection pools for DB etc are shared this way so its not an uncommon use case in day to day usage of Rust.
- scoutt 4y agoI feel your pain as I started to learn Rust recently (again) and found the same problem, coming from C. In my case, after fighting for literally days, the crate OnceCell did the job (kind of), but I felt awkward retorting to an external crate to deal with a simple static-global-initialize-once-never-touch-again piece of data.
- howinteresting 4y agoInitializing global, static state isn't simple at all -- C++ is a prime example for how a bad design for static initializers can go horribly wrong. At the very least, you need some sort of locking mechanism to deal with the potential issue where multiple threads try to initialize the variable concurrently. And you need some poisoning mechanism to prevent panics and reentrancy during initialization. Rust makes you realize "oh, yeah, I need to think about these too". In any case, OnceCell is a very reasonable way to do this and is on track for stabilization.
- marcus_cemes 4y agoI don't want to come off dogmatically defending Rust, I code little Rust in comparison to JS, and I've done C and C++ for some embedded systems. C is a very different monster to most other languages, I find people defending C to be just as proud and defensive as Rust programmers. To address some of your complaints: yes, there are a lot of concepts to understand. I like wrappers, I found it crazy that in C, you first declare a mutex_t variable, and then you specifically have to call chMtxObjectInit(*mutex_t mutex) to initialise it [1]. If you forget? UB, kernel panic sometime in the future. I think Mutex::new() is far cleaner, and it's namespaced without arbitrary function prefixes. Binaries are tiny in comparison to JS/Python with deps, they will be larger than C. Compile times aren't that slow and you can't make extra language features happen out of thin air. In C, I've found that it's commonplace to do a lot of clever and mysterious pointer and memory tricks to squeeze out performance and low resource utilisation. In embedded, there's usually a strong inclination to using "global" static variables, even declaring them inside function bodies because it "limits the visibility/scope of the variable". Not declaring a static variable inside a function is what knocked a few points off my Bachelor's robotics project. I personally don't like this. It puts a lot of pressure on the programmer to understand the order of execution, and keep a complex mental model of how the program works. Large memory allocation, such as a cache, can be hidden in just about any function, not just at the top of a file where global variables are usually defined. It sounds like what you're trying to accomplish is inherently unsafe, hence the "preaching", as in it requires the programmer's guarantee that 1) the data is fully initialised before it's accessed and 2) once the data is initialised, it's read-only and can therefore safely be accessed from other threads. C doesn't care, it will let you do a direct pointer access to a static variable with no overhead. Where's the cost? The programmer's mental model. I haven't tried, but I imagine that Rust's unsafe block will allow you to access static variables, just like in C with no overhead, effectively giving your OK to the compiler that you can vouch for the memory safety. Rust solutions: lazy_static crate (safe, runtime cost in checking if initialised on every access), RwLock<Option<T>> (safe, runtime cost to lock and unwrap the option), unsafe (no overhead, memory model cost and potentially harder debugging), extra &T function parameter (code complexity cost, "prop-drilling", cleaner imo). On modern hardware, the runtime cost is absolutely negligeable. Why would you not want to use Rust for a large project? This seems a bit contradictory to me. The safety guarantees in my opinion really pay off when the codebase is large, and it's difficult to construct that memory model, especially with a team working on different parts. Instead, you overload that work to the compiler to check the soundness of your memory access in the entire codebase. If you like C, by all means keep on using it, I enjoyed my forray into C, it's simple and satisfying, but would much prefer Rust, after spending a lot of time tracking down memory corruption. Rust's original design purpose was to reduce memory bugs in large-scale projects, not to replace C/C++ for the fun of it. We usually have a natural inclination to what we know well and have used for a long time. Feel free to correct me if something is wrong. 1: http://www.chibios.org/dokuwiki/doku.php?id=chibios:documentation:books:rt:kernel_mutexes http://www.chibios.org/dokuwiki/doku.php?id=chibios:document...
- howinteresting 4y agoPrecomputing state in a global variable works great with once_cell::sync::Lazy (which, like the rest of once_cell, is on track for stabilization). I use it all the time, and I agree that the ability to do that sort of caching is important.