How to store a void* reference to a struct in Rust?

余生颓废 提交于 2019-12-12 09:51:19

问题


I'm interacting with some C callbacks that use the standard void* userdata method to allow you to store a reference to some context (e.g. a struct). How can I store a reference to a Rust struct in a void* and still allow it to be moved around? It seems that Rust moves really are moves, i.e. this code fails (as expected):

struct Thing {
    pointer_to_self: *mut Thing,
}

fn create_thing() -> Thing {
    let mut a = Thing {
        pointer_to_self: std::ptr::null_mut(),
    };
    a.pointer_to_self = &mut a as *mut _;
    a
}

fn main() {
    let mut b = create_thing();

    assert_eq!(&mut b as *mut _, b.pointer_to_self);
}

Is there a way around this? Can I have a Rust value that doesn't change address when you move it?


回答1:


You can prevent the value from changing address by heap-allocating the object. This will cost a dereference to access it, but it will be stationary:

struct RealThing {
    // ...
}

struct Thing {
    // pointer could also be a field in RealThing, but it seems to
    // make more sense to leave only the actual payload there
    real_thing: Box<RealThing>,
    pointer_to_real: *mut RealThing,
}

fn create_thing() -> Thing {
    let mut a = Thing {
        real_thing: Box::new(RealThing {}),
        pointer_to_real: std::ptr::null_mut(),
    };
    a.pointer_to_real = a.real_thing.as_mut() as *mut _;
    a
}

fn main() {
    let mut b = create_thing();

    assert_eq!(b.real_thing.as_mut() as *mut _, b.pointer_to_real);
}

Note that you would have the same issue in C++ if you tried to use the address of an object that has been move- or copy-constructed in the meantime.

A word of warning: actually using the pointer will lead to undefined behavior unless one takes precautions to prevent the existence of multiple writable references to the same object. The UnsafeCell documentation says:

In general, transmuting an &T type into an &mut T is considered undefined behavior. The compiler makes optimizations based on the knowledge that &T is not mutably aliased or mutated, and that &mut T is unique.

It is probably safer to box RefCell<RealThing>, store an immutable pointer to the boxed cell, and convert that back to &mut RealThing by casting the pointer to &RefCell<RealThing> and calling borrow_mut() on the reference. If you then make a mistake, at least Rust will warn you by panicking.



来源:https://stackoverflow.com/questions/41089016/how-to-store-a-void-reference-to-a-struct-in-rust

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!