[英]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.