#raw-pointers #destructor #drop #macro-derive #derive #macro

ffi-destruct

为在FFI中包含原始指针的结构生成析构函数

4个版本

0.1.3 2022年10月19日
0.1.2 2022年10月18日
0.1.1 2022年10月18日
0.1.0 2022年10月17日

#191FFI

每月25次下载

Apache-2.0

20KB
126 代码行

FFI Destruct

crates badge docs badge build badge

为在FFI中包含原始指针的结构生成析构函数。

关于

Destruct derive宏将实现Drop特质并为包含原始指针的结构释放(释放)内存。这可能是FFI资源管理的一种常见程序。

安全性

所有原始指针释放操作都是不安全的且几乎未经检查。应仅释放由Rust端管理的资源。

支持类型

接受*const*mut。但当前仅支持单级指针。

类型 处理程序 注意
* c_char ::std::ffi::CString::from_raw() C风格字符串。可能的类型路径
std::ffi::c_char libc::c_char std::os::raw::c_char
* <T> ::std::boxed::Box::from_raw() Rust堆分配的任何内容。可能来自
Box::into_raw(Box::new(<T>))

示例

提供了一个包含需要手动释放的几个原始指针的结构。

use ffi_destruct::{extern_c_destructor, Destruct};
use std::ffi::*;

#[derive(Destruct)]
pub struct MyStruct {
    field: *mut std::ffi::c_char,
}

pub struct AnyOther(u32, u32);

// Struct definition here, with deriving Destruct and nullable attributes.
#[derive(Destruct)]
pub struct Structure {
    // Default is non-null.
    c_string: *const c_char,
    #[nullable]
    c_string_nullable: *mut c_char,

    other: *mut MyStruct,
    #[nullable]
    other_nullable: *mut MyStruct,

    // Do not drop this field.
    #[no_drop]
    not_dropped: *const AnyOther,

    // Raw pointer for any other things
    any: *mut AnyOther,

    // Non-pointer types are still available, and will not be added to drop().
    pub normal_int: u32,
    pub normal_string: String,
}

// (Optional) The macro here generates the destructor: destruct_structure()
extern_c_destructor!(Structure);

fn main() {
    // Some resources manually managed
    let tmp = AnyOther(1, 1);
    let tmp_ptr = Box::into_raw(Box::new(tmp));

    let my_struct = Structure {
        c_string: CString::new("Hello").unwrap().into_raw(),
        c_string_nullable: std::ptr::null_mut(),
        other: Box::into_raw(Box::new(MyStruct {
            field: CString::new("Hello").unwrap().into_raw(),
        })),
        other_nullable: std::ptr::null_mut(),
        not_dropped: tmp_ptr,
        any: Box::into_raw(Box::new(AnyOther(1, 1))),
        normal_int: 114514,
        normal_string: "Hello".to_string(),
    };

    let my_struct_ptr = Box::into_raw(Box::new(my_struct));
    // FFI calling
    unsafe {
        destruct_structure(my_struct_ptr);
    }

    // Drop the manually managed resources
    unsafe {
        let _ = Box::from_raw(tmp_ptr);
    }
}

在宏展开之后

#[macro_use]
#![feature(prelude_import)]
#![allow(dead_code)]
#[prelude_import]
use std::prelude::rust_2021::*;
#[macro_use]
extern crate std;
use ffi_destruct::{extern_c_destructor, Destruct};
use std::ffi::*;
pub struct MyStruct {
    field: *mut std::ffi::c_char,
}
impl ::std::ops::Drop for MyStruct {
    fn drop(&mut self) {
        unsafe {
            let _ = ::std::ffi::CString::from_raw(self.field as *mut ::std::ffi::c_char);
        }
    }
}
pub struct AnyOther(u32, u32);
pub struct Structure {
    c_string: *const c_char,
    #[nullable]
    c_string_nullable: *mut c_char,
    other: *mut MyStruct,
    #[nullable]
    other_nullable: *mut MyStruct,
    #[no_drop]
    not_dropped: *const AnyOther,
    any: *mut AnyOther,
    pub normal_int: u32,
    pub normal_string: String,
}
impl ::std::ops::Drop for Structure {
    fn drop(&mut self) {
        unsafe {
            let _ = ::std::ffi::CString::from_raw(
                self.c_string as *mut ::std::ffi::c_char,
            );
            if !self.c_string_nullable.is_null() {
                let _ = ::std::ffi::CString::from_raw(
                    self.c_string_nullable as *mut ::std::ffi::c_char,
                );
            }
            let _ = ::std::boxed::Box::from_raw(self.other as *mut MyStruct);
            if !self.other_nullable.is_null() {
                let _ = ::std::boxed::Box::from_raw(
                    self.other_nullable as *mut MyStruct,
                );
            }
            let _ = ::std::boxed::Box::from_raw(self.any as *mut AnyOther);
        }
    }
}
#[no_mangle]
pub unsafe extern "C" fn destruct_structure(ptr: *mut Structure) {
    if ptr.is_null() {
        return;
    }
    let _ = ::std::boxed::Box::from_raw(ptr);
}
fn main() {
    let tmp = AnyOther(1, 1);
    let tmp_ptr = Box::into_raw(Box::new(tmp));
    let my_struct = Structure {
        c_string: CString::new("Hello").unwrap().into_raw(),
        c_string_nullable: std::ptr::null_mut(),
        other: Box::into_raw(
            Box::new(MyStruct {
                field: CString::new("Hello").unwrap().into_raw(),
            }),
        ),
        other_nullable: std::ptr::null_mut(),
        not_dropped: tmp_ptr,
        any: Box::into_raw(Box::new(AnyOther(1, 1))),
        normal_int: 114514,
        normal_string: "Hello".to_string(),
    };
    let my_struct_ptr = Box::into_raw(Box::new(my_struct));
    unsafe {
        destruct_structure(my_struct_ptr);
    }
    unsafe {
        let _ = Box::from_raw(tmp_ptr);
    }
}

依赖

~2MB
~43K SLoC