PHP FFI - 将 Rust 函数返回的数组返回回 PHP

5

我需要从Rust函数中返回几个值。尝试声明一个返回数组的函数。

$ffi = FFI::cdef('float get_arr()[2];', './target/release/libphp_rust.dylib');

$array = $ffi->get_arr();

但出现了一个错误:PHP 致命错误: FFI\ParserException: 第 1 行不允许返回数组的函数在 /array.php 中:3

看起来 PHP FFI 不能直接使用数组。所以我找到了另一个解决方案。 我从 PHP 中创建了 C 数组,然后将指针传递给 Rust 代码,并使用 Rust 函数对其进行了填充:

$ffi = FFI::cdef('bool get_arr(float (*res)[2]);', './target/release/libphp_rust.dylib');

$array = $ffi->new('float[2]');

$result = $ffi->get_arr(FFI::addr($array));

if ($result) {
    var_dump($array);
} else {
    //... something went wrong
}

#[no_mangle]
pub extern fn get_arr(array_pointer: *mut [f32;2]) -> bool {
    let res = unsafe {
        assert!(!array_pointer.is_null());
        &mut *array_pointer
    };

    res[0] = 0.1;
    res[1] = 0.2;

    return true;
}

这个解决方案看起来是正确的,但我对它有一些疑虑:

  1. 传递指针到FFI是否足够安全?以后可能会遇到什么问题?
  2. Rust数组是否完全兼容C,以便我能够直接通过索引为其赋值?
  3. 是否有更好的方法来实现我所需的功能?或者关于通过FFI传递复杂数据结构的最佳实践?

谢谢

1个回答

2

目前这方面的规则仍未确定,因此您提供的示例可能存在一定风险。以下内容应该可以,但需要使用夜间版功能:

#![feature(maybe_uninit_extra)]
#![feature(ptr_as_uninit)]

// Make sure you use `extern "C"`. `extern` alone means `extern "Rust"`.
#[no_mangle]
pub extern "C" fn get_arr(array_pointer: *mut [f32; 2]) -> bool {
    let fat: *mut [f32] = array_pointer;
    let res = unsafe { fat.as_uninit_slice_mut().unwrap() };

    res[0].write(0.1);
    res[1].write(0.2);

    true
}

在稳定通道上,它只是不太优雅:
// Make sure you use `extern "C"`. `extern` alone means `extern "Rust"`.
#[no_mangle]
pub extern "C" fn get_arr(array_pointer: *mut [f32; 2]) -> bool {
    assert!(!array_pointer.is_null());
    unsafe {
        let res = array_pointer as *mut f32;
        res.add(0).write(0.1);
        res.add(1).write(0.2);
    }

    true
}

请问,这行代码的作用是什么?let fat: *mut [f32] = array_pointer; - zoryamba
当然,这只是将 *mut [f32; 2] 强制转换为 *mut [f32](一个 fat 指针),以便使用方法 <*mut [T]>::as_uninit_slice_mut()。出于某种原因,似乎 Rust 不会自动执行此操作。 - Coder-256
我有点困惑,于是进行了更多的挖掘,结果发现这实际上是一个错误(https://github.com/rust-lang/rust/issues/74679)! - Coder-256
我仍然不明白为什么我们需要在这里使用MaybeUninit,因为我的代码在没有它的情况下也可以正常工作。使用它来确保所有外部指针的安全性是一种好的实践吗?如果我从外部代码传递空指针,它会在Rust运行时初始化吗?我的理解是否正确? - zoryamba
或者它只是允许使用可空的C数据类型进行工作? - zoryamba
显示剩余7条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接