7 ms·
That seems unusual. I would assume trivial means the default approach works for most cases. Perhaps mutable global variables are not a common use case. Unsafe m
by etse 10mo ago
That seems unusual. I would assume trivial means the default approach works for most cases. Perhaps mutable global variables are not a common use case. Unsafe might make it easier, but it’s not obvious and probably undesired.
I don’t know Rust, but I’ve heard pockets of unsafe code in a code base can make it hard to trust in Rust’s guarantees. The compromise feels like the language didn’t actually solve anything.
- kibwen 10mo agoOutside of single-initialization/lazy-initialization (which are provided via safe and trivial standard library APIs: https://doc.rust-lang.org/std/sync/struct.LazyLock.html https://doc.rust-lang.org/std/sync/struct.LazyLock.html ) almost no Rust code uses global mutable variables. It's exceedingly rare to see any sort of global mutable state, and it's one of the lovely things about reading Rust code in the wild when you've spent too much of your life staring at C code whose programmers seemed to have a phobia of function arguments.
- dxxvi 10mo ago> It's exceedingly rare to see any sort of global mutable state I know a bit of Rust, so you don't need to explain in details. How to use a local cache or db connection pool in Rust (both of them, IMO, are the right use case of global mutable state)?
- therein 10mo agoWhy does that have to be global? You can still pass it around. If you don't want to clobber registers, you can still put it in a struct. I don't imagine you are trying to avoid the overhead of dereferencing a pointer.
- spacechild1 10mo agoI think a better example might be logging. How is this typically solved in Rust? Do you have to pass a Logger reference to every function that potentially wants to log something? (In C++ you would typically have functions/macros that operate on a global logger instance.)
- whytevuhuni 9mo agoIn Rust you typically use the "log" crate, which also has a global logger instance [0]. There is also "tracing" which uses thread local storage. As another comment said, global state is allowed. It just has to be proven thread-safe via Rust's Send and Sync traits, and 'static lifetime. I've used things like LazyLock and ArcSwap to achieve this in the past. [0] https://docs.rs/log/latest/log/fn.set_logger.html https://docs.rs/log/latest/log/fn.set_logger.html
- dxxvi 10mo agoI wonder what the advantage of passing it around is when it makes the argument list longer. The only advantage that I can see is that it emphasizes that this function does something with cache.
- adastra22 10mo agoYou wrap it in a mutex and then it is allowed. Global state is allowed. It just has to be thread safe.
- deleted 10mo ago[deleted]
- nu11ptr 10mo ago> I would assume trivial means the default approach works for most cases. Perhaps mutable global variables are not a common use case. Unsafe might make it easier, but it’s not obvious and probably undesired. I'm a Rust fan, and I would generally agree with this. It isn't difficult, but trivial isn't quite right either. And no, global vars aren't terribly common in Rust, and when used, are typically done via LazyLock to prevent data races on intialization. > I don’t know Rust, but I’ve heard pockets of unsafe code in a code base can make it hard to trust in Rust’s guarantees. The compromise feels like the language didn’t actually solve anything. Not true at all. First, if you aren't writing device drivers/kernels or something very low level there is a high probability your program will have zero unsafe usages in it. Even if you do, you now have an effective comment that tells you where to look if you ever get suspicious behavior. The typical Rust paradigm is to let low level crates (libraries) do the unsafe stuff for you, test it thoroughly (Miri, fuzzing, etc.), and then the community builds on these crates with their safe programs. In contrast, C/C++ programs have every statement in an "unsafe block". In Rust, you know where UB can or cannot happen.
- irishcoffee 10mo ago> Even if you do, you now have an effective comment that tells you where to look if you ever get suspicious behavior. By the time suspicious behavior happens, isn’t it kind of a critical inflection point? For example, the news about react and next that came out. Once the code is deployed, re-deploying (especially with a systems language that quite possibly lives on an air-gapped system with a lot of rigor about updates) means you might as well have used C, the dollar cost is the same.
- stouset 10mo agoAre you with a straight face saying that occasionally having a safety bug in limited unsafe areas of Rust is functionally the same as having written the entire program in an unsafe language like C? One, the dollar cost is not the same. The baseline floor of quality will be higher for a Rust program vs. a C program given equal development effort. Second, the total possible footprint of entire classes of bugs is zero thanks to design features of Rust (the borrowck, sum types, data race prevention), except in a specifically delineated areas which often total zero in the vast majority of Rust programs.
- stouset 10mo agoThe default approach is to use a container that enforces synchronization. If you need manual control, you are able to do that, you just need to explicitly opt into the responsibility that comes with it. If you use unsafe to opt out of guarantees that the compiler provides against data races, it’s no different than doing the exact same thing in a language that doesn’t protect against data races.
- bigstrat2003 10mo ago> I would assume trivial means the default approach works for most cases. I mean, it does. I'm not sure what you consider the default approach, but to me it would be to wrap the data in a Mutex struct so that any thread can access it safely. That works great for most cases. > Perhaps mutable global variables are not a common use case. I'm not sure how common they are in practice, though I would certainly argue that they shouldn't be common. Global mutable variables have been well known to be a common source of bugs for decades. > Unsafe might make it easier, but it’s not obvious and probably undesired. All rust is doing is forcing you to acknowledge the trade-offs involved. If you want safety, you need to use a synchronization mechanism to guard the data (and the language provides several). If you are ok with the risk, then use unsafe. Unsafe isn't some kind of poison that makes your program crash, and all rust programs use unsafe to some extent (because the stdlib is full of it, by necessity). The only difference between rust and C is that rust tells you right up front "hey this might bite you in the ass" and makes you acknowledge that. It doesn't make that global variable any more risky than it would've been in any other language.
- lowbloodsugar 10mo agoSo I've got a crate I built that has a type that uses unsafe. Couple of things I've learned. First, yes, my library uses unsafe, but anyone who uses it doesn't have to deal with that at all. It behaves like a normal implementation of its type, it just uses half the memory. Outside of developing this one crate, I've never used unsafe. Second, unsafe means the author is responsible for making it safe. Safe in rust means that the same rules must apply as unsafe code. It does not mean that you don't have to follow the rules. If one instead used it to violate the rules, then the code will certainly cause crashes. I can see that some programmers would just use unsafe to "get around a problem" caused by safe rust enforcing those rules, and doing so is almost guaranteed to cause crashes. If the compiler won't let you do something, and you use unsafe to do it anyway, there's going to be a crash. If instead we use unsafe to follow the rules, then it won't crash. There are tools like Miri that allow us to test that we haven't broken the rules. The fact that Miri did find two issues in my crate shows that unsafe is difficult to get right. My crate does clever bit-tricks and has object graphs, so it has to use unsafe to do things like having back pointers. These are all internal, and you can use the crate in safe rust. If we use unsafe to implement things like doubly-linked lists, then things are fine. If we use unsafe to allow multiple threads to mutate the same pointers (Against The Rules), then things are going to crash. The thing is, when you are programming in C or C++, it's the same as writing unsafe rust all the time. In C/C++, the "pocket of unsafe code" is the entire codebase. So sure, you can write safe C, like I can write safe "unsafe rust". But 99% of the code I write is safe rust. And there's no equivalent in C or C++.
- disappoint 10mo ago[flagged]