我想将 FnOnce
闭包传递给稍后要使用的对象,但我想避免任何堆分配 . 我可以通过将闭包保持在堆栈上来避免堆分配 . 但问题是我无法传递对象的引用,因为 FnOnce
call_once
消耗了闭包 . 所以我需要在没有堆分配的情况下传递一个拥有的指针(例如 Box
) .
这可能吗?我想做的是:
fn main() {
let mut scheduler = NoHeapScheduler();
// allocate the task on the stack
let task = move ||;
// somehow pass ownership of the closure, while keeping it allocated on the stack.
scheduler.add_task(StaticBox::new(task));
schedule.run();
}
据我所知,只要调度程序没有超过任务,这应该是安全的 . 有没有办法让这种情况发生?
4 回答
不 . 实际上这是非感性的,因为根据定义,堆栈对象由堆栈拥有,因此它也不能由其他东西拥有 .
除了
Box
之外还有其他拥有的指针 .目前,没有堆分配,我知道没有,但没有理由不能做到 .
在这种情况下,我设想
InlineFnOnceBox<S: Default, R, A>
用作InlineFnOnceBox<[u8; 48], (), ()>
,它包含数组本身,用作后备存储,以及指向实例化类型的FnOnce<A -> R>
v表的虚拟指针 .它需要一些小心(和
unsafe
代码)来实例化,但在其他方面似乎是可行的 .不,但您可以简单地将堆栈对象移动到调度程序中 . 您安排的每个闭包都会增加您的调度程序的大小,但它将完全自包含,甚至可以移动 .
基本思想是您的Scheduler成为一种单链表:
Scheduler
特性用于打破NoHeapScheduler
中的递归链(否则我们需要一个像可变参数泛型的特性) .为了终止链,我们还为某些无操作类型实现
Scheduler
,例如()
:现在唯一剩下的就是添加新闭包的方法 .
此方法将当前调度程序移动到新的调度程序并添加计划的闭包 .
您可以像这样使用此功能:
完整的工作示例playground
如上所述,问题的答案是“不” .
如果你传递了闭包的所有权,你必须按照定义将它移动到所有者(否则你得到的是一个参考) . 如果您只使用泛型类型进行一次回调,则可以这样做:
Playground
但是,添加多个闭包会遇到问题,因为它们都有不同的类型 .
如果您愿意在夜间编译器上允许分配和不稳定的功能,则可以使用
FnBox
. 这是like FnOnce but works with Box:Playground
我可以使用
Option
来做到这一点 . 我可以将Option
保留在堆栈上并传递一个可变引用,然后当我准备好使用闭包时,我可以使用Option::take获取闭包的所有权并使用它 .为了处理
FnOnce
的不同实现,我可以将其解析为特征并使用特征对象:然后我可以传递生活在堆栈上的特征对象,但可以通过引用来消费 .