Rust 分配数据以传递给FFI呼叫的正确方式是什么?

Rust 分配数据以传递给FFI呼叫的正确方式是什么?,rust,ffi,Rust,Ffi,之后,我把它玩得更远了一点,我想再次检查一下我的理解 我有一个被调用两次的Windows API。在第一个调用中,它返回实际out参数所需的缓冲区大小。然后,使用足够大的缓冲区再次调用它。我目前正在使用Vec作为此缓冲区的数据类型(请参见下面的示例) 代码可以工作,但我想知道这是否是正确的方法,还是最好使用像alloc::heap::allocate这样的函数直接保留一些内存,然后使用transmute将FFI的结果转换回来。同样,我的代码是有效的,但我正试图看一看幕后的情况 extern cr

之后,我把它玩得更远了一点,我想再次检查一下我的理解

我有一个被调用两次的Windows API。在第一个调用中,它返回实际out参数所需的缓冲区大小。然后,使用足够大的缓冲区再次调用它。我目前正在使用
Vec
作为此缓冲区的数据类型(请参见下面的示例)

代码可以工作,但我想知道这是否是正确的方法,还是最好使用像
alloc::heap::allocate
这样的函数直接保留一些内存,然后使用
transmute
将FFI的结果转换回来。同样,我的代码是有效的,但我正试图看一看幕后的情况

extern crate advapi32;
extern crate winapi;
extern crate widestring;
use widestring::WideCString;
use std::io::Error as IOError;
use winapi::winnt;

fn main() {
    let mut lp_buffer: Vec<winnt::WCHAR> = Vec::new();
    let mut pcb_buffer: winapi::DWORD = 0;

    let rtrn_bool = unsafe {
        advapi32::GetUserNameW(lp_buffer.as_mut_ptr(),
                               &mut pcb_buffer )
    };

    if rtrn_bool == 0 {

        match IOError::last_os_error().raw_os_error() {
            Some(122) => {
                // Resizing the buffers sizes so that the data fits in after 2nd 
                lp_buffer.resize(pcb_buffer as usize, 0 as winnt::WCHAR);
            } // This error is to be expected
            Some(e) => panic!("Unknown OS error {}", e),
            None => panic!("That should not happen"),
        }
    }


    let rtrn_bool2 = unsafe {
        advapi32::GetUserNameW(lp_buffer.as_mut_ptr(), 
                               &mut pcb_buffer )
    };

    if rtrn_bool2 == 0 {
        match IOError::last_os_error().raw_os_error() {
            Some(e) => panic!("Unknown OS error {}", e),
            None => panic!("That should not happen"),
        }
    }

    let widestr: WideCString = unsafe { WideCString::from_ptr_str(lp_buffer.as_ptr()) };

    println!("The owner of the file is {:?}", widestr.to_string_lossy());
}
理想情况下,您可以使用,因为您可以将所需的路线指定为布局的一部分:

主要的缺点是,即使在释放分配时,您也需要知道对齐方式

使用
Vec
作为一种简单的分配机制是常见的做法,但在使用它时需要小心

  • 确保您的单位是正确的——“长度”参数是项目数还是字节数
  • 如果您将
    Vec
    分解为组件,则需要
  • 跟踪长度和容量。有些人使用它来确保这两个值是相同的
  • 避免跨流-内存是由Rust分配的,必须由Rust释放。将其转换回要删除的
    Vec
  • 请注意,空的
    Vec
    不具有空指针!:

    fn main() {
        let v: Vec<u8> = Vec::new();
        println!("{:p}", v.as_ptr());
        // => 0x1
    }
    
    fn main(){
    设v:Vec=Vec::new();
    println!(“{:p}”,v.as_ptr());
    //=>0x1
    }
    

  • 对于您的具体情况,我可能建议使用
    Vec
    capacity
    ,而不是自己跟踪第二个变量。您会注意到,在第一次调用之后,您忘记了更新
    pcb\u buffer
    ,因此我非常确定代码总是会失败。这很烦人,因为它需要是一个可变的引用,所以你不能完全摆脱它

    此外,您可以只使用空格,而不是扩展
    Vec


    也不能保证第一次通话时所需的大小与第二次通话时所需的大小相同。你可以做一些循环,但是你必须担心无限循环的发生。

    这就是我想到的

    pub struct FfiObject {
        pub ptr: *mut u8,
        pub size: usize,
    }
    impl FfiObject {
        // allocate and zero memory
        pub fn new(size: usize) -> FfiObject {
            FfiObject::_from_vec(vec![0u8; size], size)
        }
        // allocate memory without zeroing
        pub fn new_uninitialized(size: usize) -> FfiObject {
            FfiObject::_from_vec(Vec::with_capacity(size), size)
        }
        fn _from_vec(mut v: Vec<u8>, size: usize) -> FfiObject {
            assert!(size > 0);
            let ptr = v.as_mut_ptr();
            std::mem::forget(v);
            FfiObject { ptr, size }
        }
    }
    impl Drop for FfiObject {
        fn drop(&mut self) {
            unsafe { std::mem::drop(Vec::from_raw_parts(self.ptr, 0, self.size)) };
        }
    }
    

    切题相关:。谢谢你的回答。所以,我想我最好还是坚持向量方程的稳定解。我将把Vec的内容更改为winnt::CHAR,这样项目的数量和长度之间就不会有差异。我想这会让事情变得更简单。不过,关于你的答案,我有两个问题:-你说我必须将内存转换回要删除的Vec到底是什么意思。-我不确定是否可以使用容量而不是第二个变量,因为这个变量也作为输出参数传递给FFI。Windows API将覆盖此变量的内容,我想用我的Vec的容量来覆盖它不是一个好主意。还是我在这方面误解了你?@Norbert我澄清了一点。仅当您首先分解了一个
    Vec
    时,才能转换回
    Vec
    ;你的案子好像没有。对于后者,我的意思是使用
    capacity
    的值,而不是
    0
    ,但是您必须有另一个变量作为引用传递。
    fn main() {
        let v: Vec<u8> = Vec::new();
        println!("{:p}", v.as_ptr());
        // => 0x1
    }
    
    pub struct FfiObject {
        pub ptr: *mut u8,
        pub size: usize,
    }
    impl FfiObject {
        // allocate and zero memory
        pub fn new(size: usize) -> FfiObject {
            FfiObject::_from_vec(vec![0u8; size], size)
        }
        // allocate memory without zeroing
        pub fn new_uninitialized(size: usize) -> FfiObject {
            FfiObject::_from_vec(Vec::with_capacity(size), size)
        }
        fn _from_vec(mut v: Vec<u8>, size: usize) -> FfiObject {
            assert!(size > 0);
            let ptr = v.as_mut_ptr();
            std::mem::forget(v);
            FfiObject { ptr, size }
        }
    }
    impl Drop for FfiObject {
        fn drop(&mut self) {
            unsafe { std::mem::drop(Vec::from_raw_parts(self.ptr, 0, self.size)) };
        }
    }
    
    // Ask the library for the size.
    let mut size: usize = 0;
    let mut success = GetObjectSize(&mut size);
    if success && size > 0 {
        // allocate and zero memory for the object
        let ffi_obj = FfiObject::new(size);
        // Pass the memory to a foreign function
        success = DoSomethingWithObject(ffi_obj.ptr, &ffi_obj.size);