需要手动掉落的 Rust 类型
Rust type that requires manual drop
如果要自动删除结构,有没有办法让 Rust 编译器出错?
示例:我正在实现一个内存池,并希望将分配手动返回到内存池以防止泄漏。下面的例子中有类似 RequireManualDrop
的东西吗?
impl MemoryPool {
pub fn allocate(&mut self) -> Option<Allocation> { /* ... */ }
pub fn free(&mut self, alloc: Allocation) { let (ptr, size) = alloc.inner.into_inner(); /* ... */ }
}
pub struct Allocation {
inner: RequireManualDrop<(*mut u8, usize)>,
}
fn valid_usage(mem: &mut MemoryPool) {
let chunk = mem.allocate();
/* ... */
mem.free(chunk);
}
/* compile error: Allocation.inner needs to be manully dropped */
fn will_have_compile_error(mem: &mut MemoryPool) {
let chunk = mem.allocate();
/* ... */
}
Rust 对此没有直接的解决方案。但是,您可以为此使用一个 nit 技巧(这个技巧取自 https://github.com/Kixunil/dont_panic)。
如果调用一个已声明但未定义的函数,链接器会报错。我们可以用它来解决掉落时的错误。通过在析构函数中调用未定义的函数,除非不调用析构函数,否则代码不会编译。
#[derive(Debug)]
pub struct Undroppable(pub i32);
impl Drop for Undroppable {
fn drop(&mut self) {
extern "C" {
// This will show (somewhat) useful error message instead of complete gibberish
#[link_name = "\n\nERROR: `Undroppable` implicitly dropped\n\n"]
fn trigger() -> !;
}
unsafe { trigger() }
}
}
pub fn manually_drop(v: Undroppable) {
let v = std::mem::ManuallyDrop::new(v);
println!("Manually dropping {v:?}...");
}
但是,请注意,虽然在这种情况下它甚至可以在调试版本中运行,但在其他情况下可能需要优化才能消除调用。展开可能会使过程更加复杂,因为它可能导致意外的隐式丢弃。例如,如果我将 manually_drop()
更改为以下看似相同的版本:
pub fn manually_drop(v: Undroppable) {
println!("Manually dropping {v:?}...");
std::mem::forget(v);
}
不行,因为println!()
可能会平仓,然后std::mem::forget(v)
就达不到了。如果一个按值采用 Undroppable
的函数必须展开,您可能别无选择,只能用 ManuallyDrop
包装它并手动确保它被正确删除(尽管您可以在函数末尾删除它, 并让 unwind case 泄漏它,因为在恐慌中泄漏大多是好的)。
如果要自动删除结构,有没有办法让 Rust 编译器出错?
示例:我正在实现一个内存池,并希望将分配手动返回到内存池以防止泄漏。下面的例子中有类似 RequireManualDrop
的东西吗?
impl MemoryPool {
pub fn allocate(&mut self) -> Option<Allocation> { /* ... */ }
pub fn free(&mut self, alloc: Allocation) { let (ptr, size) = alloc.inner.into_inner(); /* ... */ }
}
pub struct Allocation {
inner: RequireManualDrop<(*mut u8, usize)>,
}
fn valid_usage(mem: &mut MemoryPool) {
let chunk = mem.allocate();
/* ... */
mem.free(chunk);
}
/* compile error: Allocation.inner needs to be manully dropped */
fn will_have_compile_error(mem: &mut MemoryPool) {
let chunk = mem.allocate();
/* ... */
}
Rust 对此没有直接的解决方案。但是,您可以为此使用一个 nit 技巧(这个技巧取自 https://github.com/Kixunil/dont_panic)。
如果调用一个已声明但未定义的函数,链接器会报错。我们可以用它来解决掉落时的错误。通过在析构函数中调用未定义的函数,除非不调用析构函数,否则代码不会编译。
#[derive(Debug)]
pub struct Undroppable(pub i32);
impl Drop for Undroppable {
fn drop(&mut self) {
extern "C" {
// This will show (somewhat) useful error message instead of complete gibberish
#[link_name = "\n\nERROR: `Undroppable` implicitly dropped\n\n"]
fn trigger() -> !;
}
unsafe { trigger() }
}
}
pub fn manually_drop(v: Undroppable) {
let v = std::mem::ManuallyDrop::new(v);
println!("Manually dropping {v:?}...");
}
但是,请注意,虽然在这种情况下它甚至可以在调试版本中运行,但在其他情况下可能需要优化才能消除调用。展开可能会使过程更加复杂,因为它可能导致意外的隐式丢弃。例如,如果我将 manually_drop()
更改为以下看似相同的版本:
pub fn manually_drop(v: Undroppable) {
println!("Manually dropping {v:?}...");
std::mem::forget(v);
}
不行,因为println!()
可能会平仓,然后std::mem::forget(v)
就达不到了。如果一个按值采用 Undroppable
的函数必须展开,您可能别无选择,只能用 ManuallyDrop
包装它并手动确保它被正确删除(尽管您可以在函数末尾删除它, 并让 unwind case 泄漏它,因为在恐慌中泄漏大多是好的)。