2

I am working on the setting up a server example from the rust book. I will add the relevant parts here.

trait FnBox {
    fn call_box(self: Box<Self>);
}

impl<F: FnOnce()> FnBox for F {
    fn call_box(self: Box<F>) {
        (*self)()
    }
}
type Job = Box<dyn FnOnce() + Send + 'static>;

my implementation which is slightly changed from the book. It is FnBox in book rather than FnOnce(). And I call

job.call_box()

Here job is of the type Job. But this throws an error.

   |             job.call_box();
   |                 ^^^^^^^^
   |
   = note: job is a function, perhaps you wish to call it
   = note: the method `call_box` exists but the following trait bounds were not satisfied:
           `dyn std::ops::FnOnce() + std::marker::Send : FnBox`
   = help: items from traits can only be used if the trait is implemented and in scope
   = note: the following trait defines an item `call_box`, perhaps you need to implement it:
           candidate #1: `FnBox`

The trait FnBox has been implemented on all the types with FnOnce() trait. So I do not understand why it says dyn std::ops::FnOnce() + std::marker::Send : FnBox is not satisfied.

What am I missing here?

raj
  • 5,989
  • 7
  • 30
  • 62
  • 2
    Note that `FnBox` is a workaround for a limitation of older versions of Rust. In modern Rust, you can just call `job()` normally (which is, I believe, the only reason turbulencetoo's answer compiles). The book has not been updated yet. – trent Aug 01 '19 at 15:35
  • @trentcl I was actually kind of surprised to see the `(self)()` bit work after I removed the `*self` part. Is it correct to say that the code OP is trying to write used to be impossible to express correctly in old versions of Rust (without using nightly FnBox), but is now basically a no-op in newer Rust? – turbulencetoo Aug 01 '19 at 16:04
  • The implementation of ```FnOnce()``` is currently above my understanding of my language with ```extern "rust-call"``` and all. But I am looking to it. But I am surprised at the way ```self()``` actually works here. I am assuming it doesnt have anything to do with deref coercion. An unsized call be invoked in such a way is surprising. – raj Aug 01 '19 at 16:24
  • @turbulencetoo Yes, exactly. – trent Aug 01 '19 at 17:06
  • @raj You are right, deref coercion doesn't come into it: [`Box` *itself* implements `FnOnce()`](https://doc.rust-lang.org/std/ops/trait.FnOnce.html#implementors) (there's no [src] link because it's implemented by a compiler hook, not in the library). Unfortunately this is not the kind of thing you can do with your own type, at least not without `feature(unsized_locals)` on nightly, but it works fine with `Box`. – trent Aug 01 '19 at 17:12

1 Answers1

1

I think the problem is the implicit Sized bound on F in the impl for your FnBox trait, which makes a Box<dyn T> not covered under that impl.

You say

The trait FnBox has been implemented on all the types with FnOnce() trait.

But actually the trait FnBox has been implemented for only all Sized types with FnOnce() trait. The docs for Sized have more info about this.

A working example is:

trait FnBox {
    fn call_box(self: Box<Self>);
}

impl<F: FnOnce() + ?Sized> FnBox for F {
    fn call_box(self: Box<F>) {
        (self)()
    }
}
type Job = Box<dyn FnOnce() + Send + 'static>;

let job: Job = Box::new(|| println!("gwahhh"));
job.call_box();

Note that I had to remove the (*self)() in favor of (self)() because you cant move an unsized type out of a Box.

Playground

turbulencetoo
  • 3,447
  • 1
  • 27
  • 50
  • yes you are right. Size is implemented silently which i missed. But the fact that ```self()``` works is little surprising to me. – raj Aug 01 '19 at 16:27