Validity of casting a shared reference to a pointer and using both for reads

std::ptr docs state:

The result of casting a reference to a pointer is valid for as long as the underlying allocation is live and no reference (just raw pointers) is used to access the same memory. That is, reference and pointer accesses cannot be interleaved.

This quote suggests that if I cast a reference to a pointer, I cannot use that reference if I'm also using the pointer.

But is this true if I'm only using shared references, casting them to *const ptrs, and only doing reads?
Above quote does not make that distinction and suggests that this would be invalid.

In the code below a_ref is cast to a_ptr and the rest of the code uses both of them. Miri does not raise ub for this.

use std::thread;

#[derive(Clone, Copy)]
struct Ptr<T>(*const T);
unsafe impl<T> Send for Ptr<T> {}
unsafe impl<T> Sync for Ptr<T> {}

fn main() {
    unsafe {
        let a = 1;
        let a_ref = &a;
        let a_ptr = Ptr(a_ref as *const i32);

        let a_ptr2 = Ptr(&a as *const i32);
        let a_ptr3 = Ptr(&raw const a);


        println!("a_ptr: {:?}", *a_ptr.0);
        println!("a_ref: {:?}", *a_ref);

        println!("a_ptr2: {:?}", *a_ptr2.0);
        println!("a_ptr3: {:?}", *a_ptr3.0);

        // and again
        println!("a_ptr: {:?}", *a_ptr.0);
        println!("a_ref: {:?}", *a_ref);

        println!("a_ptr2: {:?}", *a_ptr2.0);
        println!("a_ptr3: {:?}", *a_ptr3.0);


        // for good mesure do it also in parallel
        thread::scope(move |s| {
            for _ in 0..10 {
                s.spawn(move || {
                    let a_ptr = a_ptr;
                    let a_ptr2 = a_ptr2;
                    let a_ptr3 = a_ptr3;

                    println!("a_ptr: {:?}", *a_ptr.0);
                    println!("a_ref: {:?}", *a_ref);

                    println!("a_ptr2: {:?}", *a_ptr2.0);
                    println!("a_ptr3: {:?}", *a_ptr3.0);

                });
            }
        });
    }
}


1 Like

A read without moving the contents? As far as I know that's valid so long as you don't access the memory in an exclusive way elsewhere. The best citation I have is pointer-to-reference conversion. (Note the distinction between &mut *ptr and &*ptr. I can't imagine it ending up otherwise, at least, if no shared/interior mutability is involved (I didn't put much thought into that use case).

1 Like

I think the docs are unclear there. Afaik it is legal to interleave & and *const reads, as long as they are from the same parent.

1 Like

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.