Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

The trait bound is not satisfied

Tags:

rust

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.

like image 327
Maik Klein Avatar asked Jun 12 '16 21:06

Maik Klein


Video Answer


1 Answers

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).

like image 110
Francis Gagné Avatar answered Nov 02 '22 00:11

Francis Gagné