如何在Rust中存储对结构的void *引用? [英] How to store a void* reference to a struct in Rust?

查看:267
本文介绍了如何在Rust中存储对结构的void *引用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在与一些使用标准void* userdata方法的C回调进行交互,以允许您存储对某些上下文(例如struct)的引用.如何在void*中存储对Rust结构的引用并仍然允许其移动?看来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 (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);
}

有没有解决的办法?我可以拥有一个Rust值,在您移动它时它不会改变地址吗?

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

推荐答案

您可以通过堆分配对象来防止值更改地址.这将取消对它的访问,但这将是固定的:

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);
}

请注意,如果您尝试使用同时移动或复制构造的对象的地址,则在C ++中也会遇到相同的问题.

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.

警告:实际上,使用指针将导致未定义的行为,除非采取预防措施以防止对同一对象存在多个可写引用. UnsafeCell文档说:

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:

通常,将&T类型转换为&mut T被认为是未定义的行为.编译器基于以下知识进行优化:&T不是可变别名或突变,并且&mut T是唯一的.

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.

RefCell<RealThing>框起来,将不可变的指针存储到装箱的单元格中,然后通过将指针转换为&RefCell<RealThing>并在引用上调用borrow_mut(),将其转换回&mut RealThing可能更安全.如果您随后犯了一个错误,至少Rust会惊慌地警告您.

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.

这篇关于如何在Rust中存储对结构的void *引用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆