Can I create my own pointer to a stack object

I would like to pass the FnOnce closure for the object to be used later, but I would like to avoid heap allocation. I can avoid heap allocation by holding closure on the stack. But the problem is that I cannot pass a reference to the object, because FnOnce call_once uses closure. Therefore, I need to pass the pointer that belongs to it (e.g. Box ) without allocating a heap.

Is it possible? I would like to do the following:

 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(); } 

As far as I know, this should be safe until the scheduler survives the task. Is there any way to do this?

+6
source share
4 answers

I can use Option for this. I can save Option on the stack and pass the modified link, and then when I am ready to use, I can use Option::take to take responsibility for closing and use it.

To handle various implementations of FnOnce , I can put this to hell and use feature objects:

 trait Callable { fn call(&mut self); } impl<F: FnOnce()> Callable for Option<F> { fn call(&mut self) { if let Some(func) = self.take() { func(); } } } 

Then I can pass objects to objects that live on the stack, but can be used with a link.

0
source

Can I create my own pointer to a stack object?

Not. This is actually insensitive, since by definition the stack object belongs to the stack, so it cannot also belong to another.

Therefore, I need to pass the pointer that belongs to it (e.g. Box) without allocating a heap.

There are pointers other than Box .

At the moment, I do not know a single one without allocating a heap, but there are no reasons why this cannot be done.

I assume that InlineFnOnceBox<S: Default, R, A> used as InlineFnOnceBox<[u8; 48], (), ()> InlineFnOnceBox<[u8; 48], (), ()> in this case, which will contain both the array itself, used as backup storage, and a virtual pointer to the FnOnce<A -> R> v-table for the type instance.

Creating an instance requires some caution (and unsafe code), but otherwise it seems possible.

+4
source

Can I create my own pointer to a stack object?

No, but you can just move the stack object to your scheduler. Your scheduler will increase in size with every closure you plan, but it will be completely autonomous, even moveable.

The basic idea is that your planner becomes a kind of singly linked list:

 pub trait Scheduler: Sized { fn run(self); } pub struct NoHeapScheduler<F: FnOnce(), T: Scheduler> { inner: T, f: F, } impl<F: FnOnce(), T: Scheduler> Scheduler for NoHeapScheduler<F, T> { fn run(self) { self.inner.run(); (self.f)() } } 

The Scheduler attribute is here to break the recursion chain in NoHeapScheduler (otherwise we need a function like varadic generics).

To complete the chain, we also implement a Scheduler for some type of no-op, for example. () :

 impl Scheduler for () { fn run(self) {} } 

Now it remains only to add new locks.

 impl<F: FnOnce(), T: Scheduler> NoHeapScheduler<F, T> { fn add_task<F2: FnOnce()>(self, f: F2) -> NoHeapScheduler<F2, Self> { NoHeapScheduler { inner: self, f: f, } } } 

This method moves the current scheduler to the new scheduler and adds a scheduled close.

You can use this function as follows:

 let scheduler = scheduler.add_task(task); 

A fully working example on a playground

+3
source

As indicated, the answer is no.

If you transfer ownership of the closure, you must, by definition, move it to the owner (otherwise you have a link). You can do this if you have only one callback using a generic type:

 pub struct NoHeapScheduler<F:FnOnce()> { f: Option<F>, } impl<F:FnOnce()> NoHeapScheduler<F> { pub fn add_task(&mut self, f: F) { self.f = Some(f); } pub fn run(&mut self) { let f = self.f.take().unwrap(); f() } } fn main() { let mut scheduler = NoHeapScheduler{ f: None }; let task = move || {}; scheduler.add_task(task); scheduler.run(); } 

Playground

However, you will have problems adding a few closures, as they all have different types.

If you want to allow distribution and unstable function in the night compiler, you can use FnBox . This is like FnOnce , but works with Box :

 #![feature(fnbox)] use std::boxed::FnBox; pub struct NoHeapScheduler { v: Vec<Box<FnBox()>>, } impl NoHeapScheduler { pub fn add_task(&mut self, f: Box<FnBox()>) { self.v.push(f); } pub fn run(&mut self) { for f in self.v.drain(0..) { f(); } } } fn main() { let mut scheduler = NoHeapScheduler{ v: Vec::new() }; let task = move || {println!("Hello,"); }; let other_task = move || {println!("world!"); }; scheduler.add_task(Box::new(task)); scheduler.add_task(Box::new(other_task)); scheduler.run(); } 

Playground

+2
source

Source: https://habr.com/ru/post/1012772/


All Articles