什么场景下需要用裸指针来传递闭包呢?
比如说需要将闭包作为参数,传递给libc中的底层函数(例如pthread的相关操作,很多都是以void * 作为参数的)
因此,假设我们有一个函数,其定义为:
fn get_lambda(ptr:*mut libc::c_void){}
此时我们有一个闭包:
let lambda = || {};
首先我们尝试,直接将lambda
变为指针传递过去。
显然不行,这里有两个问题:
- 生命周期问题,如果将指针传递过去,而lambda可能自己被drop掉了,而在另一侧再使用指针就会出现问题,所以我们应该找个办法,不能让他被析构掉。
- 即使没有生命周期问题,也会出现
*mut c_void
无法转回闭包指针的问题:let a = ||{}; let b: *const c_void = &a as *const dyn Fn() as *const c_void; let c: *const dyn Fn() =b as *const dyn Fn();
以上代码是报错的。原因也很简单,你这个是裸指针,而dyn Fn()本身的size是不确定的(可能由于多捕获了几个变量而变大),因此这个指针显然没法正常转回去。
那么就只能另想办法了。我们很容易想到可以使用Box来装载这个闭包,使用Box之后,我们有很多办法可以来解决生命周期问题,比如说使用leak
,配合手动drop
,或者使用into_raw
和 from_raw
等等:
let mut a =Box::new (||{});
let b: *mut c_void = Box::into_raw(a) as *mut dyn Fn() as *mut c_void;
let c = unsafe { Box::from_raw(b as *mut dyn Fn()) };
前面两句都是没问题的,问题还是出现在恢复闭包指针的时候。原因也和刚刚也一样,dyn Fn()的Size不确定。
那怎么办呢?由于我们恢复闭包指针的时候,是恢复称dyn Fn()的指针,导致size不确定,那么我们把这玩意儿给包在一个Size确定的东西里面不就行了?是的,又是你了,Box
let mut a =Box::new(Box::new (||{}));
let b: *mut c_void= Box::into_raw(a) as *mut Box<dyn Fn()> as *mut c_void;
let c: Box<Box<dyn Fn()>> = unsafe { Box::from_raw(b as *mut Box<dyn Fn()>)};
好了,接下来愉快的调用就行了:
(*c)();