Rust中使用裸指针传递闭包(Lambda)

什么场景下需要用裸指针来传递闭包呢?

比如说需要将闭包作为参数,传递给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_rawfrom_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)();

发表评论