19

Coroutine-rs

This is the function I am trying to call:

#[inline]
pub fn spawn<F>(f: F) -> Handle
    where F: FnOnce(&mut Coroutine) + Send + 'static
{
    Self::spawn_opts_impl(Box::new(f), Options::default())
}

I then created an enum because I actually want to send it from one thread to another, which is also why I have boxed the function. I have also matched the trait constraints.

enum Message {
    Task(Box<FnOnce(&mut Coroutine) + Send + 'static>),
}

But if I try to extract the function from a Message:

fn main(){
    let m = Message::Task(Box::new(|me| {
    }));
    let c = match m{
        Message::Task(f) => Coroutine::spawn(f)
    };
}

I get the following error:

src/main.rs:168:29: 168:45 error: the trait bound `for<'r> Box<for<'r> std::ops::FnOnce(&'r mut coroutine::asymmetric::Coroutine) + Send>: std::ops::FnOnce<(&'r mut coroutine::asymmetric::Coroutine,)>` is not satisfied [E0277]
src/main.rs:168         Message::Task(f) => Coroutine::spawn(f)
                                            ^~~~~~~~~~~~~~~~
src/main.rs:168:29: 168:45 help: run `rustc --explain E0277` to see a detailed explanation
src/main.rs:168:29: 168:45 help: the following implementations were found:
src/main.rs:168:29: 168:45 help:   <Box<std::boxed::FnBox<A, Output=R> + 'a> as std::ops::FnOnce<A>>
src/main.rs:168:29: 168:45 help:   <Box<std::boxed::FnBox<A, Output=R> + Send + 'a> as std::ops::FnOnce<A>>
src/main.rs:168:29: 168:45 note: required by `coroutine::asymmetric::Coroutine::spawn`

I have no idea what Rust is trying to tell me here. I assume that the problem is that spawn expects a non boxed function, but I get the same error if I try to deref the boxed function.

Note that at the time this question was asked, coroutine-rs doesn't build, and I fixed the errors in this fork.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Maik Klein
  • 15,548
  • 27
  • 101
  • 197

1 Answers1

8

Let's read the error message carefully:

src/main.rs:168:29: 168:45 error: the trait bound
    `for<'r>
         Box<
             for<'r> std::ops::FnOnce(
                 &'r mut coroutine::asymmetric::Coroutine
             ) + Send
         >:
     std::ops::FnOnce<
         (
             &'r mut coroutine::asymmetric::Coroutine,
         )>` is not satisfied [E0277]

Basically, you are trying to pass a Box<FnOnce> to a function that expects a type that implements FnOnce.

However, you cannot call a function that is in a Box<FnOnce>, because in order to call it, you need to pass self by value, which means that you need to dereference the Box, but that yields an unsized type, which cannot be passed by value (as of Rust 1.9).

The current workaround is to use the unstable FnBox trait instead of FnOnce. FnBox is automatically implemented for all types that implement FnOnce. Here's how we can use it:

#![feature(fnbox)]

use std::boxed::FnBox;

enum Message {
    Task(Box<FnBox(&mut Coroutine) + Send + 'static>),
}

fn main() {
    let m = Message::Task(Box::new(|me: &mut Coroutine| {
    }));
    let c = match m {
        Message::Task(f) => Coroutine::spawn(|me| f.call_box((me,)))
    };
}

Note that the call to Command::spawn receives a closure that calls the FnBox, because we can't pass the FnBox directly to Command::spawn for the reasons mentioned above. Also, I had to explicitly annotate the argument type on the first closure, otherwise the compiler complained (expected concrete lifetime, found bound lifetime parameter, which I think is a bug in the compiler).

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Francis Gagné
  • 60,274
  • 7
  • 180
  • 155
  • Thanks. Basically this means that I will have 2 function allocations and 2 functions calls right? Would it also be possible to extend `Coroutine` so that it also could accept a `Box` instead of just `FnOnce`? And then I would not have to make the detour with FnBox? – Maik Klein Jun 13 '16 at 00:00
  • Yes, there will be 2 function allocations, but the closure that calls the `FnBox` is allocated on the stack, so it's relatively cheap. Yes, there will be an additional function call. Also, `Coroutine` could probably be adapted, though you have a `Box` and `Coroutine` currently deals with `Box`, which are incompatible. – Francis Gagné Jun 13 '16 at 00:43