5

I'm trying to pass a function pointer as a callback function in a parameter, here's a stripped example from my code so far:

Rust playground code

use std::{
    fs::File,
    io::{self, BufRead, BufReader},
    path::Path,
};

use futures::{executor, Future}; // 0.3.8

type DigestCallback<R> = fn(&[u8]) -> R;

async fn consume<T>(
    path: T,
    chunk_size: usize,
    digest: DigestCallback<impl Future<Output = ()>>,
) -> io::Result<()>
where
    T: AsRef<Path>,
{
    let file = File::open(path)?;
    let mut reader = BufReader::with_capacity(chunk_size, file);
    loop {
        let buffer = reader.fill_buf()?;
        let length = buffer.len();

        if length == 0 {
            break;
        }

        digest(buffer).await;
        reader.consume(length);
    }

    Ok(())
}

async fn digest_callback(chunk: &[u8]) -> () {
    ()
}

fn main() {
    executor::block_on(consume("my_file.bin", 64, digest_callback));
}

and I'm getting this error

error[E0308]: mismatched types
  --> src/main.rs:41:51
   |
36 | async fn digest_callback(chunk: &[u8]) -> () {
   |                                           -- the `Output` of this `async fn`'s found opaque type
...
41 |     executor::block_on(consume("my_file.bin", 64, digest_callback));
   |                                                   ^^^^^^^^^^^^^^^ one type is more general than the other
   |
   = note: expected fn pointer `for<'r> fn(&'r [u8]) -> impl futures::Future`
              found fn pointer `for<'_> fn(&[u8]) -> impl futures::Future`

It works when I make it into a Vec<u8> but I'm worried about performance issues since I'm processing potentially huge files. Can someone help me understand what I'm doing wrong? How am I supposed to do this in Rust? Is there a better way of achieving this?

Update: If I remove Future and async functions, and change the function type to type DigestCallback = for<'a> fn(&'a [u8]) -> (); It seems to work. Don't know how to make it work with asynchronous functions yet though

Rust playground code


use std::{
  fs::File,
  io::{self, BufRead, BufReader},
  path::Path,
};

type DigestCallback = for<'a> fn(&'a [u8]) -> ();

fn consume<T>(
  path: T,
  chunk_size: usize,
  digest: DigestCallback,
) -> io::Result<()>
where
  T: AsRef<Path>,
{
  let file = File::open(path)?;
  let mut reader = BufReader::with_capacity(chunk_size, file);
  loop {
    let buffer = reader.fill_buf()?;
    let length = buffer.len();
    
    if length == 0 {
      break;
    }
    
    digest(buffer);
    reader.consume(length);
  }

  Ok(())
}

fn digest_callback(_chunk: &[u8]) -> () {()}

fn main() {
  let _ = consume("my_file.bin", 64, digest_callback);
}
ShadowScripter
  • 7,314
  • 4
  • 36
  • 54
  • Does this answer your question? [Why isn't \`std::mem::drop\` exactly the same as the closure |\_|() in higher-ranked trait bounds?](https://stackoverflow.com/questions/59023616/why-isnt-stdmemdrop-exactly-the-same-as-the-closure-in-higher-ranke) – E_net4 Dec 24 '20 at 10:27
  • 1
    Leaving the linked question for reference, although I see that it will not fully address this example. What happens is that you might expect the function `digest_callback` to be fully compatible with the signature `DigestCallback>`. However, like in the difference between `drop` and the closure `|_|()`, the former will not satisfy the conditions fulfilled by the latter in terms of lifetime higher-kindedness. However, turning it into a closure just to satisfy the compiler here is not possible because `async` closures are not stable yet. – E_net4 Dec 24 '20 at 10:32
  • @E_net4andapileofhats it does help me understand some of what is going on a little bit better but it doesn't fully answer the question. I wonder if the usage from your link for the function `two_uses` could work here. The return type is generally the same in all cases here, so that might be a better way to approach this – ShadowScripter Dec 24 '20 at 10:40
  • The type for `async fn digest_callback(chunk: &[u8]) -> () {()}` would be like `for <'a> fn(&[u8]) -> Future<…> + 'a`, not `fn(&[u8]) -> Future<…>`. So the error makes perfect sense. – mcarton Dec 24 '20 at 10:44

1 Answers1

0

you may try

 fn consume<T, F >(
  path: T,
  chunk_size: usize,
  digest: F,
) -> io::Result<()>
where
  T: AsRef<Path>,
  F: for <'c> Fn(&'c mut Vec<u8>)->Pin<Box<dyn Future<Output = ()> + 'c>>
左晓良
  • 47
  • 1