原始指针未产生预期的效果

时间:2019-07-15 21:40:08

标签: pointers memory rust

#![feature(ptr_internals)]

use core::ptr::Unique;

struct PtrWrapper {
    id: usize,
    self_reference: Unique<Self>
}

impl PtrWrapper {
    fn new() -> Self {
        let dummy = unsafe {Unique::new_unchecked(std::ptr::null_mut::<PtrWrapper>())};
        let mut ret = Self {id:0, self_reference: dummy };
        let new_ptr = &mut ret as *mut Self;
        debug_print(new_ptr);
        ret.self_reference = Unique::new(new_ptr).unwrap();
        debug_print(ret.self_reference.as_ptr());
        ret
    }

    fn get_id(&self) -> usize {
        self.id.clone()
    }
}

fn main() {
    println!("START");
    let mut wrapper = PtrWrapper::new();
    wrapper.id = 10;

    let ptr = wrapper.self_reference.as_ptr();
    unsafe {
        (*ptr).id += 30;
        println!("The next print isn't 40? Garbage bytes");
        debug_print(ptr);

        let tmp = &mut wrapper as *mut PtrWrapper;
        (*tmp).id += 500;

        println!("The next print isn't 540?");
        debug_print(tmp);
    }

    println!("Below debug_print is proof of undefined behavior! Garbage bytes\n");
    debug_print(wrapper.self_reference.as_ptr());


    debug_print(&mut wrapper as *mut PtrWrapper);

    debug_print_move(wrapper);

    println!("Why is the assertion below false?");
    assert_eq!(unsafe{(*ptr).id}, 540);
}

fn debug_print_move(mut wrapper: PtrWrapper) {
    debug_print(&mut wrapper as *mut PtrWrapper);
}

fn debug_print(ptr: *mut PtrWrapper) {
    println!("Address: {:p}", ptr);
    println!("ID: {}\n", unsafe {(*ptr).get_id()});
}

上面的代码应该在锈迹斑斑的操场上使用每晚选择的版本进行编译。注意控制台输出。

我的问题是:为什么间歇性结果不等于我期望它们相等的值?在下面的情况下,没有同时进行多个访问(单线程),因此没有任何数据争用。但是,堆栈上存在对象的隐式多个可变版本。

正如预期的那样,指针的存储位置随着tmp变量的变化以及整个对象移入debug_print_move时的变化。看来使用tmp指针可以按预期工作(即加500),但是,从Unique<PtrWrapper>对象获得的指针似乎指向了内存中无关的位置。

1 个答案:

答案 0 :(得分:0)

按照Stargateur的建议,为了解决此问题,我们需要固定需要自参考的对象。我最终使用:

pin-api = "0.2.1"

在cargo.toml中,而不是std::pin::pin中。接下来,我设置此结构及其实现:

#![feature(ptr_internals, pin_into_inner, optin_builtin_traits)]
// not available on rust-playground
extern crate pin_api;

use pin_api::{boxed::PinBox, marker::Unpin, mem::Pin};

///test
pub struct PtrWrapper<T>
where
    T: std::fmt::Debug,
{
    ///tmp
    pub obj: T,
    /// pinned object
    pub self_reference: *mut Self,
}

impl<T> !Unpin for PtrWrapper<T> where T: std::fmt::Debug {}

impl<T> PtrWrapper<T>
where
    T: std::fmt::Debug,
{
    ///test
    pub fn new(obj: T) -> Self {
        Self {
            obj,
            self_reference: std::ptr::null_mut(),
        }
    }

    ///test
    pub fn init(mut self: Pin<PtrWrapper<T>>) {
        let mut this: &mut PtrWrapper<T> = unsafe { Pin::get_mut(&mut self) };
        this.self_reference = this as *mut Self;
    }

    /// Debug print
    pub fn print_obj(&self) {
        println!("Obj value: {:#?}", self.obj);
    }
}

最后,测试功能:

fn main2() {
    unsafe {
        println!("START");

        let mut wrapper = PinBox::new(PtrWrapper::new(10));
        wrapper.as_pin().init();

        let m = wrapper.as_pin().self_reference;

        (*m).obj += 30;
        println!("The next print is 40");
        debug_print(m);

        let tmp = wrapper.as_pin().self_reference;
        (*tmp).obj += 500;

        println!("The next print is 540?");
        debug_print(tmp);

        debug_print(wrapper.self_reference);

        let cpy = PinBox::get_mut(&mut wrapper);
        debug_print_move(cpy);

        std::mem::drop(wrapper);
        println!("Works!");
        assert_eq!(unsafe { (*m).obj }, 540);
    }
}

fn debug_print_move<T>(mut wrapper: &mut PtrWrapper<T>)
where
    T: std::fmt::Debug,
{
    debug_print(&mut *wrapper as *mut PtrWrapper<T>);
}

fn debug_print<T>(ptr: *mut PtrWrapper<T>)
where
    T: std::fmt::Debug,
{
    println!("Address: {:p}", ptr);
    unsafe { (*ptr).print_obj() };
}

在旁注中,pin api在锈操场上不存在。您仍然可以使用std::pin::Pin,但是需要进一步的自定义。