I am trying to pass a GObject
to a FFI function from libsecret-1 which requires it be in a GList
. I have some code that almost compiles, but it complains about the lifetime of the borrowed vaue being too short and not outliving the function. The object itself was created from the FFI using the wrapper!
macro from the glib crate for Rust. Here is a minimum-viable reproduction that produces the error I am getting:
我正在尝试将一个GObject从libret-1传递给FFI函数,这要求它出现在glist中。我有一些几乎可以编译的代码,但它抱怨借用的vaue的生命周期太短,并且没有超过函数的寿命。对象本身是使用包装器从FFI创建的!从油滑的板条箱里取来的宏,用于生锈。这是一个最小可行的复制,它产生了我得到的错误:
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
}
The GList should just be a shared reference that is only needed for the duration of the call to the FFI function so I am not sure why it thinks the lifetime need to live beyond the function. The error triggers on the &arr[..]
due to it being borrowed for longer than the life of arr
.
GLIST应该只是一个仅在调用FFI函数期间需要的共享引用,所以我不确定为什么它认为生存期需要存在于函数之外。错误在&arr[..]上触发因为它被借的时间比阿尔的寿命还长。
Also, any tips on how to make this less wordy would be nice.
此外,任何关于如何让这一点不那么冗长的提示都会很好。
更多回答
@cafce25 Apologies for that oversight. I've added the missing dependencies and it compiles for me in its own, dedicated file now. I originally was going to add the use-case for slice, but that pulls in some FFI code to call a C function with several arguments and I felt added a lot of extra noise to the MVE above. Here is the function I am calling: developer-old.gnome.org/libsecret/unstable/…. I can call that function and pass ptr::null_mut() as the second argument to it as long as I comment out the line creating the GList slice.
@afce25为这一疏忽道歉。我已经添加了缺少的依赖项,现在它会在它自己的专用文件中为我编译。我最初打算添加Slice的用例,但这会引入一些FFI代码来调用带有几个参数的C函数,我觉得这会给上面的MVE增加很多额外的噪音。下面是我要调用的函数:Developer-old.gnome.org/libret/unstant/…。只要我注释掉创建GLIST切片的行,我就可以调用该函数并将ptr::ullmut()作为第二个参数传递给它。
The problem is you specify the caller can choose a lifetime 'a
for which all of these bounds have to hold, but the caller can only specify lifetimes that live from before the call to after the function returned. But arr
doesn't live that long.
问题是,您指定调用者可以选择所有这些界限都必须保持的生存期,但调用者只能指定从调用前到函数返回后的生存期。但arr没有那么长的生存期。
You can use higher ranked trait bounds (HRTB) to specify that W
has to implement ToGlibPtr
for any lifetime, not just lifetimes specified by the caller.
您可以使用更高级别的特征界限(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
}
The error you're encountering is due to Rust's borrow checker ensuring that the borrowed value (&arr[..]) doesn't outlive the reference itself (arr). In this case, Rust doesn't know how long the reference arr will be needed, so it assumes that you're trying to use the borrowed value outside its expected lifetime.
您遇到的错误是由于Rust的借入检查器确保借入的值(&arr[..])不会超过引用本身(ARR)。在本例中,Rust不知道需要引用arr多长时间,因此它假定您正在尝试使用超出其预期生命周期的借用值。
To address this issue, you can use the std::mem::ManuallyDrop wrapper to temporarily extend the lifetime of the arr variable. Here's a modified version of your code that avoids the lifetime issue and is less wordy:
要解决此问题,您可以使用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);
}
更多回答
This exactly resolved the issue with a good explanation. It also fixes the code given in the other answer. When were HRTBs added to stable Rust?
这正好解决了一个很好的解释问题。它还修复了另一个答案中给出的代码。什么时候HRTBs被添加到稳定的Rust中?
I'm just surprised I don't recall running into them yet. Your solution was perfect, though I did tweak it slightly. I broke up the where clause to only use the HRTB for the trait that needed it. The rest of the code which I omitted from the OP worked as is.
我只是很惊讶我还不记得我遇到过他们。你的解决方案是完美的,尽管我稍微调整了一下。我分解了WHERE子句,以便只将HRTB用于需要它的特征。我在操作中省略的其余代码按原样工作。
Well HRTB are rarely needed. That's why you don't see them often, their main usecase the Fn
family of traits has special syntax sugar which hides them and other usecases are few and far apart.
嗯,很少需要HRTB。这就是为什么你不经常看到它们的原因,它们的主要用法FN家族的特征有特殊的语法糖分,隐藏了它们,而其他用例很少,而且相距甚远。
Hmm, copying your example verbatim still triggered the error for me. The borrowed value from arr.as_ref()
which was bound at ManuallyDrop::new([obj])
does not live long enough. I am using Rust 1.71.0.
嗯,逐字复制您的示例仍然会触发我的错误。从arr.as_ref()借来的值绑定到ManuallyDrop::new([obj])时寿命不够长。我使用的是铁锈1.71.0。
@penguin359 yea that's what I would have expected, when the arr
goes out of scope so do references to it, that doesn't get changed by ManuallyDrop
I wouldn't expect this to work.
@penguin359年,这就是我所期望的,当arr超出范围时,所以要引用它,这不会被ManuallyDrop更改我不会期望这会起作用。
Hmm.. how about trying use the ToGlibPtr trait to convert each GObject in the slice to a *mut GObject, and then manually construct the GList?
嗯..。尝试使用ToGlibPtr特征将切片中的每个GObject转换为*mut GObject,然后手动构建GList如何?
Something like that: // Function to create a GList from a slice of GObject references fn create_glist(objects: &[&dyn IsA<glib::Object>]) -> *mut GList { let mut glist_ptr: *mut GList = std::ptr::null_mut(); for obj in objects.iter() { // Convert each object to *mut GObject let obj_ptr = obj.to_glib_full(); // Append it to the GList glist_ptr = unsafe { g_list_append(glist_ptr, obj_ptr as *mut c_void) }; } glist_ptr }
类似的东西://从GObject引用的切片创建GList的函数fn create_glist(objects:&[&dyn IsA])-> *mut GList { let mut glist_ptr:*mut GList = std::ptr::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 }
@user3527282 I had considered that, but I looked at the source for the ToGlibContainerFromSlice trait and that is basically what it already does so I don't think doing manually. It uses the ToGlibPtr trait to convert each entry and then adds them to a GList with the native glib API.
@user3527282我曾考虑过这一点,但我查看了ToGlibContainerFromSlice特征的源代码,这基本上就是它已经做的,所以我不认为手动做这件事。它使用ToGlibPtr特征来转换每个条目,然后使用本地Glib API将它们添加到glist中。
我是一名优秀的程序员,十分优秀!