提问人:penguin359 提问时间:9/10/2023 最后编辑:penguin359 更新时间:9/11/2023 访问量:49
如何将 GObjects 的切片作为 GList 从 Rust 转换为 FFI 函数?
How to convert a slice of GObjects as a GList to a FFI function from Rust?
问:
我正在尝试将 a 传递给 libsecret-1 的 FFI 函数,该函数要求它在 .我有一些几乎可以编译的代码,但它抱怨借来的 vaue 的生命周期太短并且没有超过函数的寿命。对象本身是使用 Rust 的 glib crate 中的宏从 FFI 创建的。这是一个产生我得到的错误的最小可行复制:GObject
GList
wrapper!
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
此外,任何关于如何使它不那么冗长的提示都会很好。
答:
-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是的,这就是我所期望的,当超出范围时,对它的引用也会改变,这不会被我期望这起作用而改变。arr
ManuallyDrop
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
问题在于,您指定调用方可以选择所有这些边界都必须保留的生存期,但调用方只能指定从调用之前到函数返回之后的生存期。但活不了那么久。'a
arr
您可以使用更高排名的特征边界 (HRTB) 来指定必须在任何生存期内实现的特征边界,而不仅仅是调用方指定的生存期。W
ToGlibPtr
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
评论