@jarkko I agree with OnceCell, why not just use an Option? I haven't used unsafecell, but I could see a reason for using it. The other ones, I use a lot. Especially Arc.
Theyre pretty essential for holding trait objects. Its probably good that you don't rely on them though since its heap allocation which could be sloe if you do it too often.
@jarkko they're quite often good at generating runtime errors ;)
@jarkko I've never used RefCell to be honest, but mostly because my interior mutability is mostly multi-threaded and that would be a `Mutex`.
You won't come across `UnsafeCell` unless you build interior mutability types from scratch.
Regarding `OnceCell`: I've used the concurrent variant of that, `OnceLock` a lot, which was previously called `OnceCell` from the once_cell library. It's just if you want something global that can't be constructed at compile time but needs lazy initialization.
@jarkko I need to correct myself: A concurrent RefCell would be more of an RwLock than a Mutex.
@jarkko Arc isn't a substitute for RwLock - it's for sharing. Want to access an RwLock (or a Mutex, or anything really) from two different places? You need Arc<RwLock<TheThing>>. Rc is an optimisation of Arc you can use when said sharing doesn't cross a thread boundary.
@FSMaxB This actually did not work
$ cargo b
warning: /home/jarkko/work/github.com/jarkkojs/polkavm/Cargo.toml: unused manifest key: workspace.lints.rust.unexpected_cfgs.check-cfg
Compiling polkavm v0.15.0 (/home/jarkko/work/github.com/jarkkojs/polkavm/crates/polkavm)
error[E0308]: mismatched types
--> crates/polkavm/src/sandbox/linux.rs:1596:13
|
1596 | vmctx,
| ^^^^^ expected `RwLock<VmCtx>`, found `RwLock<&mut VmCtx>`
|
= note: expected struct `RwLock<VmCtx>`
found struct `RwLock<&mut VmCtx>`
For more information about this error, try `rustc --explain E0308`.
error: could not compile `polkavm` (lib) due to previous error
I’m sure I can solve it just putting a note in order to not spread FUD ;-)
@FSMaxB Found a pattern for my current problem that compiles and works.
Creation:
let vmctx = RwLock::new(unsafe { Arc::from_raw(vmctx_mmap.as_mut_ptr().cast::<VmCtx>()) });
Example use:
Arc::get_mut(&mut self.vmctx.write().unwrap()).unwrap().regs[reg as usize] = value;
This whole topic gained my interest because I incorrectly first used Box here. In this case Arc is the solution because it does “just drop” instead of “drop + free” (Box).
@jarkko I'm not sure I get what you're saying. An Arc is still just a Box internally: https://doc.rust-lang.org/stable/src/alloc/sync.rs.html#387-392
(Or rather, the box is used to do the heap allocation but afterwards turned into a pointer)
On drop of the last strong reference, it calls `drop_in_place` to drop the data. But deallocation only happens when the last `Weak` reference drops.
So this is where your free happens: https://doc.rust-lang.org/stable/src/alloc/sync.rs.html#3167-3169
@jarkko Also from_raw might not be doing what you think it is:
"The raw pointer must have been previously returned by a call to Arc<U>::into_raw with the following requirements: ..." (https://doc.rust-lang.org/stable/std/sync/struct.Arc.html#method.from_raw)
So you can't just take raw memory from anywhere. It must have been an Arc previously.
What it does is calculate backwards from the pointer to get the start of the Arc.
@jarkko I usually find Jon Gjengset's "decrusting rust" series quite enlightening.
I myself did do some Rust presentations on YouTube (as part of meetups) but don't find the time anymore. There is a video about traits and about futures though.
@jarkko yeah it's a building block. And Cell/RefCell are for situations when the static borrow checker is too restrictive - they defer checking to runtime (and panic when you violate the borrowing rules). Should be used sparingly.