* mut Option<T> -> Option<*mut T>?

Is it possible to convert * mut Option<T> to Option<*mut T> ?

Context: I'm trying to build rc_arena. Progress so far:

// based on https://github.com/thomcc/rust-typed-arena/tree/master

#[cfg(not(feature = "std"))]
extern crate alloc;

#[cfg(any(feature = "std", test))]
extern crate core;

use alloc::rc::{Rc, Weak};
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;

use core::{
    cell::{Cell, RefCell},
    cmp, mem,
    ptr::NonNull,
};
use std::ptr;

type T = std::rc::Rc<usize>;

#[repr(C)]
struct My_RcBox<T> {
    strong: Cell<usize>,
    free_list_idx: My_Id, // what to append to free list
    value: T,
}

pub struct My_Rc<T> {
    arena: Weak<Arena<T>>,
    ptr: NonNull<My_RcBox<T>>,
}

#[derive(Clone, Copy)]
pub struct My_Id {
    vec_id: usize,
    loc_in_vec: usize,
}

struct ChunkList<T> {
    current: Vec<Option<My_RcBox<T>>>,
    rest: Vec<Vec<Option<My_RcBox<T>>>>,
    free_list: Vec<My_Id>,
}

impl<T> ChunkList<T> {
    unsafe fn get_mut(&mut self, idx: My_Id) -> *mut Option<My_RcBox<T>> {
        if idx.vec_id == self.rest.len() {
            self.current.as_mut_ptr().offset(idx.loc_in_vec as isize)
        } else {
            self.rest[idx.vec_id].as_mut_ptr().offset(idx.loc_in_vec as isize)
        }
    }

    fn double_size(&mut self) {
        let double_cap = self.current.capacity().checked_mul(2).expect("capacity overflow");
        let chunk = mem::replace(&mut self.current, Vec::with_capacity(double_cap));
        self.rest.push(chunk);
    }

    fn alloc(&mut self, v: T) -> *const My_RcBox {
        if let Some(idx) = self.free_list.pop() {
            unsafe {
                let mut t = self.get_mut(idx);
                *t = Some(My_RcBox {
                    strong: Cell::new(0),
                    free_list_idx: idx,
                    value: v,
                });
            }
            return;
        }

        if self.current.len() == self.current.capacity() {
            self.double_size();
        }

        self.current.push(Some(My_RcBox {
            strong: Cell::new(0),
            free_list_idx: My_Id {
                vec_id: self.rest.len(),
                loc_in_vec: self.current.len(),
            },
            value: v,
        }))
    }
}

impl<T> My_Rc<T> {
    pub unsafe fn get_mut_unchecked(this: &mut Self) -> &mut T {
        unsafe { &mut (*this.ptr.as_ptr()).value }
    }

    unsafe fn dec_strong(&mut self) -> usize {
        let t = &self.ptr.as_ref().strong;
        let n = t.get() - 1;
        t.set(n);
        n
    }

    pub fn as_ref(&self) -> Option<&T> {
        if self.arena.strong_count() == 0 {
            // arena has been dropped
            None
        } else {
            Some(unsafe { &self.ptr.as_ref().value })
        }
    }
}

impl<T> Drop for My_Rc<T> {
    fn drop(&mut self) {
        let cur_strong_count = unsafe { self.dec_strong() };
        if (cur_strong_count == 0) {
            unsafe {
                ptr::drop_in_place(My_Rc::get_mut_unchecked(self));
            }
            if let Some(arena) = self.arena.upgrade() {
                // arena still active
                let free_list_idx = unsafe { self.ptr.as_ref().free_list_idx };
                arena.chunks.borrow_mut().free_list.push(free_list_idx);
            }
        }
    }
}

// Initial size in bytes.
const INITIAL_SIZE: usize = 1024;
// Minimum capacity. Must be larger than 0.
const MIN_CAPACITY: usize = 1;

pub struct Arena<T> {
    chunks: RefCell<ChunkList<T>>,
}

impl<T> Arena<T> {
    pub fn new() -> Arena<T> {
        let size = cmp::max(1, mem::size_of::<T>());
        Arena::with_capacity(INITIAL_SIZE / size)
    }

    pub fn with_capacity(n: usize) -> Arena<T> {
        let n = cmp::max(MIN_CAPACITY, n);
        Arena {
            chunks: RefCell::new(ChunkList {
                current: Vec::with_capacity(n),
                rest: Vec::new(),
                free_list: vec![],
            }),
        }
    }

    #[inline]
    pub fn alloc(self: Rc<Self>, v: T) -> My_Rc<T> {
        let t = self.chunks.borrow_mut().alloc(v);
        // self.alloc_fast_path(value).unwrap_or_else(|value| self.alloc_slow_path(value))
    }
}

Something like this? I'd feel more comfortable about soundness without the intermediate &muts, but I'm not sure how to do that.

unsafe fn transpose_ptr_opt<T>(popt: *mut Option<T>)->Option<*mut T> {
    (*popt).as_mut().map(|x: &mut T| x as *mut T)
}
1 Like

This is admittedly changing the question :slight_smile: ; at this point I'm convinced this is the wrong problem to solve, and instead I should be re-designing to get rid of the Option<My_RcBox<T>> into something that does not use the Option. I.e. see Help redesigning attempt at rc_arena for full details on what led to this particular problem.

You may find option_payload_ptr interesting. It returns a pointer to the payload of an Option without intermediate &mut.

1 Like

That one has been removed on nightly in favor of offset_of! since that can now address enum payloads.

4 Likes

I think that the solution with offset_of! will look something like this:

#![feature(offset_of_enum)]
#![feature(offset_of)]
use core::mem::offset_of;

unsafe fn transpose_ptr_opt<T>(popt: *mut Option<T>)->Option<*mut T> {
    if popt.is_null() { return None; }
    match *popt {
        None => None,
        Some(_) => {
            let off = offset_of!(Option<T>, Some.0) as isize;
            Some(popt.byte_offset(off) as *mut T)
        }
    }
}
3 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.