Conversation

Jarkko Sakkinen

Edited 1 month ago
{Ref,Unsafe,Once,}Cell is one of the more confusing parts for me in Rust and I almost never end up using them because I don't really know what they are good at.

Also, have to admit that the use of Arc is rare and the use of Rc is never.

#rust #rustlang
4
1
4

@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.

1
0
0
I still use e.g. PhantomData and it took less than 30 minutes to fully grab it (when I discovered it in 2022).
0
0
0
@SeanOMik I rarely use Arc, and rather end up using e.g. RwLock or something. It does not end up to be the best option often in the end. So yeah have to admit that it is also fuzzy... And I really just don't get so far any of the cell types. I can admit that I don't understand them :-)
2
1
0
@SeanOMik And also: I don't think there is much to agree on with me as I don't understand what they actually are. So going specifics with one cell type is not really the answer here, neither is how much you happen to use them. I.e. not a popularity vote...
0
0
0

@jarkko they're quite often good at generating runtime errors ;)

0
0
1

@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.

2
0
1

@jarkko I need to correct myself: A concurrent RefCell would be more of an RwLock than a Mutex.

0
0
1
@FSMaxB

I regularly use Lazy from https://docs.rs/spin/latest/spin/lazy/struct.Lazy.html to do lazy initialization so I guess I've missed the std options.

I can actually think of one possible use case for Arc that would stick and I might need to do that. If I understand these primitives correctly Box and Arc have different ways on how they die:

1. Box: drop + free
2. Arc: drop

So for e.g. syscall reserved resource you might want to use Arc inside struct instead of Box. Both have a nice helper "from_raw" for raw memory.
3
0
0

Jarkko Sakkinen

Edited 1 month ago
@FSMaxB LOL I often use this search to find out what I might want to use from Rust library: https://doc.rust-lang.org/stable/std/?search=from_raw :D

I tend to end up to doing something in the edge of what Rust mm understands internally so having that tools is always value in robustness.
0
0
0

@FSMaxB Nah I tested and I could ignore both and instead do:

        let foo = RwLock::new(unsafe { &mut *foo_raw.as_mut_ptr().cast::<Foo>() });

:-) If I have a Drop trait for Foo it’s pretty much what it needs to be.

2
0
0
@FSMaxB I.e. I can put that then to RwLock<Foo> field in a struct. I tested and it worked! It might be also that I sometimes have redone some of e.g. cell functionality with PhantomData, i.e. I'm using "atoms" instead of "molecules" and thus I might be blind for the need perhaps... Still would be nice to see e.g. a presentation that would actually nail these (not a Rustbro Youtube tutorial video crap but something more in-depth).
1
0
0

@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.

1
0
1
@dngrs OK fair enough. I rarely have anything with multiple owners. That is a good example I think thanks! I get that.

Cell types are still totally alien topic but maybe that will uncover some day :-)
1
1
0
@dngrs Someone explained me "in C" what UnsafeCell is: it is like restrict + noalias in C compiler set to a pointer. And it is mostly for constructing other Cell entities. So yeah now I get much better what they are :-)
1
0
1

Jarkko Sakkinen

Edited 1 month ago

@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 ;-)

1
0
0

Jarkko Sakkinen

Edited 1 month ago

@FSMaxB I solve that by

  1. vmctx: *mut VmCtx
  2. vmctx: RwLock::new(unsafe { vmctx_mmap.as_mut_ptr().cast::<VmCtx>() }),

Not pretty but this at least works :-) If there is some day something nicer that I get I’ll fix it up…

1
0
0

@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).

1
0
0
@FSMaxB better to stop spamming here :-) anyway this was the best documentation for me ever for cells: "noalias/restrict". That nails them...
0
0
0

@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

2
0
1

@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.

0
0
0

@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.

1
0
1
@FSMaxB i'm not sure what i'm doing exactly yet i.e. learning ;-)
0
0
0
@FSMaxB thanks i learned a lot from your answers! highly appreciate this!
0
0
1

@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.

1
0
1
@dngrs thanks for responses. you're always beginner in something :-)
1
0
0