pub trait Forget {
fn forget_me_as_ptr(self) -> *const Self
where
Self: Sized,
{
let p = addr_of!(self);
forget(self);
p
}
fn forget_me_as_mut_ptr(self) -> *mut Self
where
Self: Sized,
{
self.forget_me_as_ptr() as *mut Self
}
fn forget_me_as_ref(self) -> &'static Self
where
Self: Sized,
{
self.forget_me_as_mut_ref()
}
fn forget_me_as_mut_ref(mut self) -> &'static mut Self
where
Self: Sized,
{
let p = addr_of_mut!(self);
forget(self);
unsafe { p.as_mut().unwrap() }
}
}
pub fn default<T: Forget + Default>() -> &'static T {
T::default().forget_me_as_ref()
}
#[derive(Debug)]
struct Player {
health: u8,
name: String,
aliases: Vec<String>,
}
impl Drop for Player {
fn drop(&mut self) {
println!("Dropping [{}, {self:p}]", type_name::<Self>());
}
}
impl Display for Player {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"[{}, {self:p}] = name: {}, health: {}, aliases: {:#?}",
type_name::<Self>(),
self.name,
self.health,
self.aliases
)
}
}
impl Forget for Player {}
impl Default for Player {
fn default() -> Self {
Self {
health: 100,
name: "default".into(),
aliases: vec!["default".to_string()],
}
}
}
fn main() {
let p = default::<Player>();
println!("{p}");
}
字符串
产出:
$ cargo r
Finished dev [unoptimized + debuginfo] target(s) in 0.00s
Running `target/debug/static_factory`
Segmentation fault (core dumped)
型
在上面的代码片段中,为什么它是segfaulting?std::mem::forget
只是获取所有权,并不运行析构函数/drop,而析构函数/drop可以用来创建生存期为静态的引用。这种逻辑的缺陷在哪里?我是Rust的新手,有点想了解引用,struct T的示例,指针之间的关系。上面的代码从一个天真的想法看起来逻辑上是正确的。请解释并更正。
3条答案
按热度按时间ldfqzlk81#
使用
std::mem::forget
可以避免运行Drop
实现,但由于self
是一个局部变量,它的内存仍然会被回收并用于其他事情。要获得所需的行为,您需要将其移动到一个不会被重用的稳定地址。您可以通过将其放入
Box
中,获取指向它的指针,然后忽略该框来实现此目的。幸运的是,已经有一个函数可以实现:Box::leak
。类似于:字符串
fgw7neuy2#
局部变量,就像一个函数的参数一样,存在于堆栈中,无论
drop
是否在它的内存中被调用,都不会有什么不同,因为一旦你离开这个函数,任何指向它的指针都会变成悬空的。因此,当p
指向本地内存(例如参数self
)并返回结果时,unsafe { p.as_mut().unwrap() }
是不可靠的。w80xi6nr3#
我的嫌疑人是跟线的:
字符串
T::default()
在堆栈上创建一个临时对象,我们为它创建一个引用。一旦函数调用结束,对应于堆栈分配对象的内存就被乱码了。如果对象是在heap上创建的,我认为这会消失。