Info
This post is auto-generated from RSS feed The Rust Programming Language Forum - Latest topics. Source: Is my usage of unsafe ok?
I had a type that was essentially Rc<[RefCell<T>]>
, but I wanted to reduce the need for runtime checks and the extra memory used for each item. I couldn't figure out how to construct Rc<RefCell<[T]>>
, so I tried to come up with an alternative to RefCell.
mod swap_cell {
use std::cell::UnsafeCell;
use std::ptr::swap;
pub struct SwapCell<T> {
inner: UnsafeCell<T>
}
impl<T> SwapCell<T> {
pub fn new(value: T) -> Self {
SwapCell {
inner: UnsafeCell::new(value)
}
}
pub fn swap(&self, value: &mut T) {
unsafe {
// [#1]
swap(self.inner.get(), value);
}
}
}
impl<T: Clone> SwapCell<T> {
pub fn clone_inner(&self) -> T {
let inner_ref = unsafe {
// [#2]
self.inner.get().as_ref().unwrap()
};
inner_ref.clone()
}
}
}
Here's what I came up with, I think it doesn't have any UB problems; SwapCell::inner
is private, so it shouldn't be possible for more than one reference to it to exist. The only way to access it is by swapping it with another value, or cloning it. I believe both unsafe blocks [#1]
and [#2]
should be fine, but I would appreciate some feedback in case I actually did something unsafe.
Here's some sample code demonstrating how I plan on using it.
use std::rc::Rc;
use std::cell::RefCell;
fn main() {
let my_list = MyList::new([MyObject::None, MyObject::None, MyObject::None]
.into_iter().map(|i| swap_cell::SwapCell::new(i)).collect());
my_list.set(0, MyObject::Number(5.0));
my_list.set(1, MyObject::FixedSizedList(my_list.clone()));
my_list.set(2, MyObject::Number(7.0));
my_list.set(1, MyObject::Number(6.0));
}
#[derive(Clone)]
enum MyObject {
None,
List(Rc<RefCell<Vec<MyObject>>>),
FixedSizedList(MyList),
Number(f64),
// ...
}
// fixed sized list, Rc<[T]> has less indirection than Rc<Vec<T>>
#[derive(Clone)]
struct MyList {
// uses a lot of memory per item
// items: Rc<[RefCell<T>]>
// difficult to construct (only [T; N] vs from Vec<T>)
// items: Rc<RefCell<[T]>>
// the solution I came up with
items: Rc<[swap_cell::SwapCell<MyObject>]>
}
impl MyList {
fn new(items: Vec<swap_cell::SwapCell<MyObject>>) -> Self {
Self {
items: Rc::from(items),
}
}
fn get(&self, offset: usize) -> Option<MyObject> {
Some(self.items.get(offset)?.clone_inner())
}
fn set(&self, offset: usize, mut obj: MyObject) -> Option<MyObject> {
self.items.get(offset)?.swap(&mut obj);
Some(obj)
}
}
7 posts - 3 participants
🏷️ Rust_feed