如何将 GObjects 的切片作为 GList 从 Rust 转换为 FFI 函数?

How to convert a slice of GObjects as a GList to a FFI function from Rust?

提问人:penguin359 提问时间:9/10/2023 最后编辑:penguin359 更新时间:9/11/2023 访问量:49

问:

我正在尝试将 a 传递给 libsecret-1 的 FFI 函数,该函数要求它在 .我有一些几乎可以编译的代码,但它抱怨借来的 vaue 的生命周期太短并且没有超过函数的寿命。对象本身是使用 Rust 的 glib crate 中的宏从 FFI 创建的。这是一个产生我得到的错误的最小可行复制:GObjectGListwrapper!

extern crate glib;                                                                                                                                                                            
                                                                                                                                                                                              
use glib::ffi::GList;
use glib::translate::{ToGlibPtr, GlibPtrDefault, ToGlibContainerFromSlice};

pub fn lock_object_test<'a, W: GlibPtrDefault +
  ToGlibPtr<'a, <W as GlibPtrDefault>::GlibType>>(obj: &'a W) {
    let arr = [obj];
    let slice: (*mut GList, <&W as ToGlibContainerFromSlice<'a, *mut GList>>::Storage) = 
        ToGlibContainerFromSlice::to_glib_none_from_slice(&arr[..]);
    // Would pass slice.0 here as a *mut GList argument to FFI
}

GList 应该只是一个共享引用,仅在调用 FFI 函数的持续时间内需要,所以我不确定为什么它认为生存期需要超越函数。由于借用的时间长于 的寿命长,因此会触发该错误。&arr[..]arr

此外,任何关于如何使它不那么冗长的提示都会很好。

ffi glib

评论

1赞 penguin359 9/11/2023
@cafce25 对于这一疏忽,我们深表歉意。我已经添加了缺少的依赖项,它现在在自己的专用文件中为我编译。我本来打算添加 slice 的用例,但这会拉入一些 FFI 代码来调用带有多个参数的 C 函数,我觉得上面的 MVE 增加了很多额外的噪音。这是我调用的函数:developer-old.gnome.org/libsecret/unstable/...。我可以调用该函数并将 ptr::null_mut() 作为第二个参数传递给它,只要我注释掉创建 GList 切片的行即可。

答:

-3赞 user3527282 9/10/2023 #1

你遇到的错误是由于 Rust 的借用检查器确保借用的值 (&arr[..]) 不会超过引用本身 (arr)。在这种情况下,Rust 不知道引用 arr 需要多长时间,因此它假设您正在尝试在其预期生命周期之外使用借用的值。

要解决此问题,可以使用 std::mem::ManuallyDrop 包装器来临时延长 arr 变量的生存期。下面是代码的修改版本,它避免了生存期问题,并且不那么冗长:

use glib::translate::*;
use glib::GlibPtrDefault;
use libc::GList;
use std::mem::ManuallyDrop;

pub fn lock_object_test<'a, W>(obj: &'a W)
where
    W: GlibPtrDefault + ToGlibPtr<'a, <W as GlibPtrDefault>::GlibType>,
{
    // Create a ManuallyDrop to extend the lifetime of 'arr'.
    let arr = ManuallyDrop::new([obj]);

    // Convert the ManuallyDrop reference to a GList.
    let slice = arr.as_ref();
    let slice_ptr: (*mut GList, <&W as ToGlibContainerFromSlice<'a, *mut GList>>::Storage) =
        ToGlibContainerFromSlice::to_glib_none_from_slice(slice);

    // Pass slice_ptr.0 as a *mut GList argument to FFI.
    // ...
}

fn main() {
    // Example usage:
    // let obj = ...; // Create your GObject here.
    // lock_object_test(&obj);
}

评论

0赞 penguin359 9/10/2023
嗯,逐字复制您的示例仍然为我触发了错误。被绑定的借入价值的寿命不够长。我正在使用 Rust 1.71.0。arr.as_ref()ManuallyDrop::new([obj])
0赞 cafce25 9/10/2023
@penguin359是的,这就是我所期望的,当超出范围时,对它的引用也会改变,这不会被我期望这起作用而改变。arrManuallyDrop
0赞 user3527282 9/10/2023
嗯。。尝试使用 ToGlibPtr trait 将切片中的每个 GObject 转换为 *mut GObject,然后手动构造 GList 怎么样?
0赞 user3527282 9/10/2023
类似这样的事情: // 从 GObject 引用的切片创建 GList 的函数 fn create_glist(objects: &[&dyn IsA<glib::Object>]) -> *mut GList { let mut glist_ptr: *mut GList = std::p tr::null_mut(); for obj in objects.iter() { // 将每个对象转换为 *mut GObject let obj_ptr = obj.to_glib_full(); // 将其附加到 GList glist_ptr = unsafe { g_list_append(glist_ptr, obj_ptr as *mut c_void) };} glist_ptr }
0赞 penguin359 9/11/2023
@user3527282我已经考虑过这一点,但我查看了 ToGlibContainerFromSlice 特征的源代码,这基本上就是它已经做的事情,所以我认为不是手动操作。它使用 ToGlibPtr 特征来转换每个条目,然后使用本机 glib API 将它们添加到 GList。
3赞 cafce25 9/10/2023 #2

问题在于,您指定调用方可以选择所有这些边界都必须保留的生存期,但调用方只能指定从调用之前到函数返回之后的生存期。但活不了那么久。'aarr

您可以使用更高排名的特征边界 (HRTB) 来指定必须在任何生存期内实现的特征边界,而不仅仅是调用方指定的生存期。WToGlibPtr

use glib::ffi::GList;
use glib::translate::{GlibPtrDefault, ToGlibPtr, ToGlibContainerFromSlice};
pub fn lock_object_test<W> (obj: &W)
where
    for<'a> W: GlibPtrDefault + ToGlibPtr<'a, <W as GlibPtrDefault>::GlibType>
{
    let arr = [obj];
    let (ptr, _zstorage): (*mut GList, <&W as ToGlibContainerFromSlice<*mut GList>>::Storage) = 
        ToGlibContainerFromSlice::to_glib_none_from_slice(&arr[..]);
    // pass ptr here as a *mut GList argument to FFI
}

评论

0赞 penguin359 9/11/2023
这恰恰解决了这个问题,并得到了很好的解释。它还修复了另一个答案中给出的代码。HRTB 是什么时候添加到稳定的 Rust 中的?
0赞 cafce25 9/11/2023
自 1.0 以来,它们一直存在。它们是在 2014 年 11 月添加
0赞 penguin359 9/14/2023
我只是很惊讶我不记得遇到过他们。你的解决方案是完美的,尽管我确实稍微调整了一下。我打破了 where 子句,只将 HRTB 用于需要它的特征。我在 OP 中省略的其余代码按原样工作。
0赞 cafce25 9/14/2023
嗯,很少需要HRTB。这就是为什么你不经常看到它们的原因,它们的主要用例,特征家族,有特殊的语法糖,隐藏了它们,而其他用例很少,而且相距甚远。Fn