如何在 Rust 中存储对结构的 void* 引用?
How to store a void* reference to a struct in Rust?
我正在与一些使用标准 void* userdata
方法的 C 回调进行交互,以允许您存储对某些上下文(例如结构)的引用。如何在 void*
中存储对 Rust 结构的引用并仍然允许它四处移动?看起来 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);
}
有办法解决这个问题吗?我可以有一个在你移动它时不会改变地址的 Rust 值吗?
您可以通过堆分配对象来防止值更改地址。这将需要取消引用才能访问它,但它将是固定的:
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++ 中也会遇到同样的问题。
一句警告:实际上使用 指针将导致未定义的行为,除非采取预防措施以防止存在对同一对象的多个可写引用。 UnsafeCell
文档说:
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>
并调用 [= 将其转换回 &mut RealThing
19=] 在参考文献中。如果你随后犯了错误,至少 Rust 会通过恐慌来警告你。
我正在与一些使用标准 void* userdata
方法的 C 回调进行交互,以允许您存储对某些上下文(例如结构)的引用。如何在 void*
中存储对 Rust 结构的引用并仍然允许它四处移动?看起来 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);
}
有办法解决这个问题吗?我可以有一个在你移动它时不会改变地址的 Rust 值吗?
您可以通过堆分配对象来防止值更改地址。这将需要取消引用才能访问它,但它将是固定的:
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++ 中也会遇到同样的问题。
一句警告:实际上使用 指针将导致未定义的行为,除非采取预防措施以防止存在对同一对象的多个可写引用。 UnsafeCell
文档说:
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>
并调用 [= 将其转换回 &mut RealThing
19=] 在参考文献中。如果你随后犯了错误,至少 Rust 会通过恐慌来警告你。