4 ms·
Yeah, I've seen lazy_static. From the documenation [1]: > The Deref implementation uses a hidden static variable that is guarded by an atomic check on each ac
by richard_todd 6y ago
Yeah, I've seen lazy_static. From the documenation [1]:
> The Deref implementation uses a hidden static variable that is guarded by an atomic check on each access.
That's totally unnecessary in the scenarios I'm talking about, but it's needed to make Rust happy. Also, it's not totally clear to me that I can feed a lazy_static information derived from main() like command-line arguments, though I did not try too hard to figure that part out. I just wound up plumbing arguments through all my functions.
[1]: https://docs.rs/lazy_static/1.4.0/lazy_static/ https://docs.rs/lazy_static/1.4.0/lazy_static/
- ChrisSD 6y agoYou want OnceCell's unsync type for that: https://docs.rs/once_cell/1.4.0/once_cell/unsync/struct.OnceCell.html https://docs.rs/once_cell/1.4.0/once_cell/unsync/struct.Once... Assuming you're dead set against thread local storage.
- richard_todd 6y agoIt's not about "dead set," it's just a thought experiment. Setting global constant data at the beginning of a program, before "the real work" starts, is totally safe to do, but there's not a language I'm aware of that lets me explain this to the compiler. Either you get a C-style free-for-all, a Rust-style series of road-blocks, or a managed-language with runtime overhead. Imagine a Rust where a static variable (not mut) could be modified in `main` in a type of unsafe {} block. Then, access to those statics in the rest of the program, even across threads should be safe and incur no runtime checks. That would be similar to what I'd like to try. The unsync type you linked says it isn't thread-safe. If you convert it to a sync type, then it has extra runtime checks. Both sync and unsync make you unwrap Option<T>'s to get the values, which again would not be necessary if I could communicate to the compiler that I am going to set up some global constants, and they will all be ready by the end of some lexical block. Just thinking out loud.
- heinrich5991 6y agoYou can do that in Rust by creating your own abstraction around `UnsafeCell`. Say `InitializeCell`: use std::cell::UnsafeCell; use std::mem::MaybeUninit; use std::ops; pub struct InitializeCell<T> { inner: UnsafeCell<MaybeUninit<T>>, } unsafe impl<T> Sync for InitializeCell<T> {} impl<T> InitializeCell<T> { pub const unsafe fn new_uninitialized() -> InitializeCell<T> { InitializeCell { inner: UnsafeCell::new(MaybeUninit::uninit()), } } pub const fn new(init: T) -> InitializeCell<T> { InitializeCell { inner: UnsafeCell::new(MaybeUninit::new(init)), } } pub unsafe fn init(&self, init: T) { (*self.inner.get()) = MaybeUninit::new(init); } } impl<T> ops::Deref for InitializeCell<T> { type Target = T; fn deref(&self) -> &T { unsafe { &*(*self.inner.get()).as_ptr() } } } static VALUE: InitializeCell<i32> = unsafe { InitializeCell::new_uninitialized() }; static OTHER_VALUE: InitializeCell<i32> = InitializeCell::new(6); fn main() { unsafe { VALUE.init(5); } println!("{}", *VALUE); println!("{}", *OTHER_VALUE); }
- richard_todd 6y agoThat's actually really nice. It does appear that Rust will peel all the layers away when opt-level is 2 or higher. I could live with that. You can (re)set the value from anywhere, but at least it has to be marked unsafe. Fair enough! When I tried to do this a couple years ago (with mem::uninitialized), I failed to get something usable. I think I was also concerned that having to use unsafe for the reads would prohibit compiler optimizations, even if it could be wrapped in a safe wrapper as you did here. Maybe that was worrying for nothing. Maybe it's time to try Rust again.
- deleted 6y ago[deleted]