我有一个可以从Vec<u8>
或&[u8]
构建的Image结构。
它表示C库中的图像对象(ffi模块)。
struct Image { ptr: *mut c_void };
impl Image {
fn from_vec(vec: Vec<u8>) -> Image {
// transfer ownership to gobject system
let ptr = unsafe {
ffi::new(
vec.as_ptr() as *const c_void,
vec.len(),
..
)
};
std::mem::forget(vec);
Image { ptr }
}
fn from_ref(data: &[u8]) -> Image {
// gobject doesn't free data on Drop
let ptr = unsafe {
ffi::new_ref(
data.as_ptr() as *const c_void,
data.len(),
..
)
};
Image { ptr }
}
fn resize(&self, ..) -> Image {
let new_ptr = unsafe { ffi::resize(self.ptr) };
Image { new_ptr }
}
}
impl Drop for Image {
fn drop(&mut self) {
unsafe {
ffi::g_object_unref(self.ptr as *mut c_void);
}
}
}
Image结构只有原始指针而且没有借位,因此编译器不会对resize操作的输出设置生命周期约束。
用矢量,这没关系:
let img1 = Image::from_vec(pixels); // consume pixels
let img2 = img1.resize(..);
return img2;
// when img2 is released, gobject system will release pixels as well
然而,有了参考,这是一个问题:
let pixels = Vec::new(..);
let img1 = Image::from_ref(&pixels);
let img2 = img1.resize(..)
return img2;
// danger: img2's gobject has a raw pointer to pixels
编译器没有抱怨,但为了防止这种情况,我希望编译器通过添加生命周期来抱怨。
我知道一个工作解决方案是拥有和借用两个版本的Image。 (如String /&amp; str)。但是,我不想重复相同的代码,这些代码仅在返回类型中有所不同:
impl OwnedImage {
fn resize(..) -> OwnedImage {
let new_ptr = unsafe { ffi::resize(self.ptr) };
OwnedImage{ptr:new_ptr}
}
}
// ScopedImage needs a PhantomData.
struct ScopedImage<'a> { ptr: *mut c_void, marker: PhantomData<&'a ()> }
impl<'a> ScopedImage<'a> {
fn resize(..) -> ScopedImage<'a> {
let new_ptr = unsafe { ffi::resize(self.ptr) };
ScopedImage{ptr:new_ptr, PhantomData}
}
}
let pixels = Vec::new(..);
let img1 = ScopedImage::from_ref(&pixels);
let img2 = img1.resize(..);
return img2; // error, as I intended.
与&amp; str / String不同,两种类型的区别仅在于编译器是否会在某些情况下抱怨。
我的问题是,是否可以将两种类型合并为一个带有生命周期参数的类型。
我的第一个想法是有两个生命周期&#39; a和&#b; b,其中&#39; a表示自己的范围,&b; b表示返回对象的范围。 对于参考图片,我想强制执行&#39; a ==&#39; b但我不确定如何实现。
// for vec, 'a!='b. for ref, 'a=='b
struct Image<'a, 'b> { ptr, ?? }
// this type parameter relationship is
// enforced at the construction
from_vec(..) -> Image<'a,'a>
from_ref<'b> (&'a data) -> Image<'a,'b>
resize<'b>(&self, ..) -> Image<'b>
或有一生:
type R = (Image:'a or Image:'b);
resize(&self, ..) -> R // R: return type, decided on construction
或者拆分为两个结构OwnedImage
和ScopedImage
并在特征中实现操作:
trait ImageTrait<'a> {
type OutputImage: 'a;
fn resize(..) -> Self::OutputImage {
..
}
}
impl<'a> ImageTrait<'a> for OwnedImage {
type OutputImage = OwnedImage;
}
impl<'a, 'b> ImageTrait<'b> for ScopedImage {
type OutputImage = ScopedImage;
}
或者,将生锈期限作为类型关联进行搜索&#39;给我这个RFC: https://github.com/rust-lang/rfcs/pull/1598 (我正在读这个。这适用于我的情况吗?)
这是我第一次编写具有复杂泛型和生命周期的严重Rust代码。 我实际上并不是在问哪个更好(虽然我不知道他们的优点/缺点以及哪些是惯用的),我甚至不知道哪些选项是可能的。
答案 0 :(得分:2)
<强> STRUCT 强>
pub struct Image<'a> {
pub c: *mut ffi::Image,
marker: PhantomData<&'a()>,
}
取消分配回调
pub unsafe extern "C" fn cleanup(ptr: *mut ffi::Image, user_data: *mut c_void) {
let b: Box<Box<[u8]>> = Box::from_raw(user_data as *mut Box<[u8]>);
println!(" >>>> releasing slice of len {}", b.len());
drop(b);
}
参考构造函数
impl<'a> Image<'a> {
pub fn from_memory_reference(buf: &'a [u8] /* ... */) -> Result<Image, Box<Error>> {
let c = unsafe {
ffi::image_new_from_memory(
buf.as_ptr() as *const c_void,
// ...
)
};
Ok(Image {
ptr: c,
PhantomData,
})
}
}
拥有的构造函数
解决方案是让参数'a
保持不足。
impl<'a> Image<'a> {
pub fn from_memory(buf: Vec<u8> /* ... */) -> Result<Image<'a>, Box<Error>> {
let b: Box<[_]> = buf.into_boxed_slice();
let c = unsafe {
ffi::image_new_from_memory(
b.as_ptr() as *const c_void,
// ...
)
};
let bb: Box<Box<_>> = Box::new(b);
let raw: *mut c_void = Box::into_raw(bb) as *mut c_void;
unsafe {
let callback: unsafe extern "C" fn() = ::std::mem::transmute(cleanup as *const ());
ffi::g_signal_connect_data(
c as *mut c_void,
"close_signal\0".as_ptr() as *const c_char,
Some(callback),
raw,
None,
ffi::GConnectFlags::G_CONNECT_AFTER,
);
};
Ok(Image {
ptr: c,
PhantomData,
})
}
}
<强>操作强>
fn resize(&self, scale: f64) -> Result<Image, Box<Error>> {
// ...
}
参考测试
let _img: Image = {
let pixels = vec![0; 256 * 256 * 3];
Image::from_memory_reference(&pixels, /* ... */).unwrap()
//~^ ERROR `pixels` does not live long enough
};
拥有测试
let _img: Image = {
let pixels = vec![0; 256 * 256 * 3];
Image::from_memory(pixels, /* ... */).unwrap()
}; // Ok
缺点是,在编写API时,我需要完全了解生命周期的省略规则,否则它可能会默默地允许不良用法。