简体   繁体   English

如何在Rust中存储一个结构的void *引用?

[英]How to store a void* reference to a struct in Rust?

I'm interacting with some C callbacks that use the standard void* userdata method to allow you to store a reference to some context (eg a struct). 我正在与一些使用标准void* userdata方法的C回调进行交互,以允许您存储对某些上下文的引用(例如结构)。 How can I store a reference to a Rust struct in a void* and still allow it to be moved around? 如何在void*存储Rust结构的引用并仍然允许它被移动? It seems that Rust moves really are moves, ie this code fails (as expected): 似乎Rust移动确实是移动,即此代码失败(如预期):

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? 移动时,我可以使用不会更改地址的Rust值吗?

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. 请注意,如果您尝试使用在此期间已移动或复制构造的对象的地址,则在C ++中会出现相同的问题。

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: UnsafeCell文档说:

In general, transmuting an &T type into an &mut T is considered undefined behavior. 通常,将&T类型转换为&mut T被认为是未定义的行为。 The compiler makes optimizations based on the knowledge that &T is not mutably aliased or mutated, and that &mut T is unique. 编译器基于&T不可变别名或突变的知识进行优化,并且&mut T是唯一的。

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. 盒装RefCell<RealThing>可能更安全,存储一个指向盒装单元格的不可变指针,并通过将指针转换为&RefCell<RealThing>并在引用上调用borrow_mut()将其转换回&mut RealThing If you then make a mistake, at least Rust will warn you by panicking. 如果你犯了一个错误,至少Rust会因恐慌而警告你。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM