Why Arc<dyn Fn(dyn FnOnce(&mut [u8]), usize) -> Result<(), ()> + Send + Sync> doesn't have syze known at compile time

use std::sync::Arc;
pub type A = Arc<dyn Fn(dyn FnOnce(&mut [u8]), usize) -> Result<(), ()> + Send + Sync>;

pub struct B {
    a: A,
}

Gives

   Compiling playground v0.0.1 (/playground)
error[E0277]: the size for values of type `(dyn for<'r> FnOnce(&'r mut [u8]) + 'static)` cannot be known at compilation time
 --> src/lib.rs:5:8
  |
5 |     a: A,
  |        ^ doesn't have a size known at compile-time
  |
  = help: the trait `Sized` is not implemented for `(dyn for<'r> FnOnce(&'r mut [u8]) + 'static)`
  = note: only the last element of a tuple may have a dynamically sized type

I think Arc has a syze know at compile time. dyn Fn has, at least when I do pub type A = Arc<dyn Fn() -> Result<(), ()> + Send + Sync>;. However, when I put dyn FnOnce(&mut [u8]) inside dyn Dn, the size cannot be known.

Why pub type A = Arc<dyn Fn() -> Result<(), ()> + Send + Sync>; is ok but not pub type A = Arc<dyn Fn(dyn FnOnce(&mut [u8]), usize) -> Result<(), ()> + Send + Sync>;?

dyn Fn, or any other dyn Trait, does not have a known size at compile time. Arc<dyn Trait>, however, does, because the dyn Trait is behind a pointer, so the size of the Arc is always just the size of that pointer, plus a second pointer pointing to information about the actual runtime type.

The problem with Arc<dyn Fn(dyn FnOnce... is that function parameters have to be sized, so the outer Fn cannot take the inner dyn FnOnce directly as an argument.

The error message could use some improvement though.

I have an struct that implements the trait A which has the function fn consume. I want to pass a callback to this struct, to be called by fn consume. Something like this:

pub type OnVirtualTunWrite = Arc<dyn Fn(?, usize) -> Result<(), VirtualTunWriteError> + Send + Sync>;

It's on an Arc because it's shared between threads.

struct A {
    on_virtual_tun_write: OnVirtualTunWrite
}

impl S for A {
    fn consume<R, F>(self, _timestamp: Instant, len: usize, f: F) -> smoltcp::Result<R>
    where
        F: FnOnce(&mut [u8]) -> smoltcp::Result<R>,
    {
        let mut lower = self.lower.as_ref().borrow_mut();
        //I should send this f to self.on_virtual_tun_write
        (self.on_virtual_tun_write)(f, len);
        //return the appropriate result here

OnVirtualTunWrite is a closure that should receive the f,len from fn consume and then use it like this:

let my_on_virtual_tun_write = Arc::new(|?, len| -> ?{
    let mut buffer = Vec::new(len);
    buffer.resize(len);
    //fills buffer with data
    f(buffer);
})

How can I make my OnVirtualTunWrite?

I tried Arc<dyn Fn(dyn FnOnce(&mut [u8]), usize) -> Result<(), ()> + Send + Sync> but it won't work because dyn Fn must have arguments with size know at compile time.

You can pass a dyn FnOnce by putting it inside a Box. For example:

Arc<dyn Fn(Box<dyn FnOnce(&mut [u8])>, usize) -> Result<(), ()> + Send + Sync>
1 Like

I guess this is why Box has size known at compile time, right?

there's still a small problem: how do I return -> smoltcp::Result<R> in OnVirtualTunWrite if OnVirtualTunWrite can't possibly know R?

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.