我在其他情况下发生过这种情况,但不能重复。
基本上,有时当你有一个*mut Foo
,并且使用Foo
将其变成ptr::read()
时,有时你的掉胶似乎不会运行。
这是一个可重复的示例,它在不使用FFI的情况下在游戏围栏上运行(这是您将原始指针提升回对象的主要原因):http://is.gd/X6vdAK
use std::ptr::read;
use std::mem::forget;
#[repr(C)]
struct Foo {
id: i32,
dispose:Option<Box<Fn(Foo) + 'static>> //'
}
impl Foo {
pub fn new(id:i32) -> Foo {
return Foo {
id: id,
dispose: None
};
}
pub fn release(mut self) {
if self.dispose.is_some() {
self.dispose.take().unwrap()(self);
}
}
}
impl Drop for Foo {
fn drop(&mut self) {
println!("Discarding a foo with dispose of {:?}", self.dispose.is_some());
}
}
#[repr(C)]
struct Goblin {
foo:Foo,
angry: bool,
friends: i32
}
#[repr(C)]
struct Bat {
foo:Foo,
hungry: bool
}
#[repr(C)]
struct Dragon {
foo:Foo,
lairs: i32
}
trait IsFoo {
fn foo(&mut self) -> &mut Foo;
fn as_foo<T: IsFoo>(mut self) -> Foo where Self: Sized {
let ptr:*const Foo = self.foo() as *mut Foo;
{
self.foo().dispose = Some(Box::new(|&:foo:Foo| {
println!("Incoming release for id {}", foo.id);
unsafe {
let tmp = &foo as *const Foo as *const T; // Avoid ICE. :(
let mut instance:T = read::<T>(tmp);
println!("Dropping instance with id: {}", instance.foo().id);
drop(instance);
}
}));
}
unsafe {
let rtn = read(ptr);
forget(self);
return rtn;
}
}
}
impl IsFoo for Bat { fn foo(&mut self) -> &mut Foo { return &mut self.foo; } }
impl IsFoo for Goblin { fn foo(&mut self) -> &mut Foo { return &mut self.foo; } }
impl IsFoo for Dragon { fn foo(&mut self) -> &mut Foo { return &mut self.foo; } }
// Test drops work
impl Drop for Bat { fn drop(&mut self) { println!("Dropped a Bat"); } }
impl Drop for Goblin { fn drop(&mut self) { println!("Dropped a Goblin....!"); } }
impl Drop for Dragon { fn drop(&mut self) { println!("Dropped a Dragon"); } }
fn main() {
{
// Generic collection
let monsters:Vec<Foo> = vec!(
Bat { foo: Foo::new(1), hungry: true }.as_foo::<Bat>(),
Goblin { foo: Foo::new(2), angry: true, friends: 100 }.as_foo::<Goblin>(),
Dragon { foo: Foo::new(3), lairs: 33 }.as_foo::<Dragon>()
);
println!("Vector exists without dropping contents");
// Cleanup
for m in monsters.into_iter() {
println!("Dropping an instance: {}", m.id);
m.release();
}
}
}
这里的输出是:
Vector exists without dropping contents
Dropping an instance: 1
Incoming release for id 1
Dropping instance with id: 1
Discarding a foo with dispose of false
Dropping an instance: 2
Incoming release for id 2
Dropping instance with id: 2
Discarding a foo with dispose of false
Dropping an instance: 3
Incoming release for id 3
Dropping instance with id: 3
Discarding a foo with dispose of false
即。虽然我将引用*mut Foo
转换为封闭中的T
,但Goblin
,Dragon
&amp; Bat
无法运行。
如果稍微调整代码,例如http://is.gd/mRzj8H,您可以运行这些(或其中一些):
Vector exists without dropping contents
Dropping an instance: 1
Dropped a Bat
Dropping an instance: 2 <--- WTF, the others work but this one doesn't?
Dropping an instance: 3
Dropped a Dragon
我在这里看到某种种族情况的文物,还是更复杂的东西?
答案 0 :(得分:2)
尝试使用不同的优化级别编译程序。 (在Rust围栏中有一个下拉列表;默认值为-O2
。)您将看到输出不一致。在这种情况下,您将调用未定义的行为。
当您向Vec
添加项目时,您可以创建包含Bat
的{{1}}对象,然后只在Foo
中添加Foo
。 Vec
不再存在。 Bat
取值IsFoo::as_foo
,这意味着它需要拥有它。 Bat
在Bat
结束时被有效删除,但您通过调用as_foo
来抑制掉落胶水。
在你的&#34;处置&#34; lambda,你试图通过将forget
的指针转换为指向Bat
的指针来获得Foo
。这是未定义的行为,因为该向量仅包含Bat
,而不是整个Foo
。请记住,离开Bat
时Bat
已被销毁。
使用IsFoo::as_foo
和-O2
,该计划可能根本不会创建-O3
,而Vec
,{{ 1}}和Bat
值在堆栈上仍然完好无损。但是,Dragon
没有Goblin
,而其他两个都有,所以这可能就是为什么析构函数不会只运行那个。
答案 1 :(得分:0)
在Rust 1.0 Drop
类型不支持#[repr(C)]
。
这是因为Drop
为结构添加了一个隐藏标志,并且只有在设置了该标志时才运行析构函数。
这是一种已知的语言疣,将来可能会通过跟踪带外标记来改变。
有#[unsafe_no_drop_flag]
删除Drop
标志,但是Rust不保证析构函数被调用多少次(你的析构函数必须能够安全地运行已经存在的对象)破坏)。