I'm trying to use a generic function pointer with a trait bound, and pass it to a function:
use std::fs::File;
use std::io::{BufRead, BufReader};
type ReaderFn<R: BufRead> = fn(R);
fn read_file<R: BufRead>(rdr: R) {
for line in rdr.lines() {
println!("{}", line.unwrap());
}
}
fn call<R: BufRead>(func: ReaderFn<R>) {
let f = File::open("/tmp").unwrap();
let rdr = BufReader::new(f);
// this works
read_file(rdr);
// this doesn't
func(rdr);
}
But I've got this error:
error[E0308]: mismatched types
--> src/main.rs:20:10
|
12 | fn call<R: BufRead>(func: ReaderFn<R>) {
| - this type parameter
...
20 | func(rdr);
| ^^^ expected type parameter `R`, found struct `BufReader`
|
= note: expected type parameter `R`
found struct `BufReader<File>`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0308`.
error: could not compile `playground`
To learn more, run the command again with --verbose.
I don't know why it works calling a simple func but not with the function pointer. I tried with a
This means that the caller can pick any type R that implements BufRead and provide a func that takes R as an argument. In the body of the function, you try to pass a BufReader to func, which might not be the R that the caller picked.
This is the syntax you need if you're always going to be passing a BufReader to func:
It fails to compile because your function, semantically, isn't really generic:
fn call<R: BufRead>(func: ReaderFn<R>) {
let f = File::open("/tmp").unwrap();
let rdr = BufReader::new(f);
// this works
read_file(rdr);
// this doesn't
func(rdr);
}
R would always be BufReader, so there's no point in making it generic.
There's also this problematic usage of your function:
What should R be? Should it be BufReader because that's what is used in the call to func in the function call? Or should R be &[u8] because that's what the caller chose?
It seems that what you want is a function that wraps a Reader in a BufReader and then performs some action with it. The following might be what you want:
// other imports here...
use std::io::Read;
fn call2<R: Read>(func: ReaderFn<BufReader<R>>, r: R) {
let rdr = BufReader::new(r);
func(rdr);
}
fn call<F: Fn(R), R: BufRead>(func: F) {
let f = File::open("/tmp").unwrap();
let rdr = BufReader::new(f);
// this works
read_file(rdr);
// this doesn't
func(rdr);
}
gives the same error. The error isn't related to the function you're taking as a parameter. The error is in the generic type R.
If you'll only ever take a BufReader, I think you should either remove the generic type completely and use
Or add another argument to the function to pass in the BufRead:
That T has to be specified as a concrete type somewhere in your program. Is that inside the implementation of call() or somewhere else? If it's somewhere else, how is that information given to call()?
Maybe you have multiple call() implementations, and they all use different T's?
Let me elaborate a little bit. Here is my function:
///Just a wrapper function for a file.
pub fn lookup(&mut self, wrapper: &mut Wrapper) -> Result<Vec<ChildData>, AppError> {
// open target file
let file = File::open(&self.path)?;
// if file is compressed, we need to call a specific reader
// create a specific reader for each compression scheme
match self.compression {
CompressionScheme::Gzip => {
let decoder = GzDecoder::new(file);
let reader = BufReader::new(decoder);
self.lookup_from_reader(reader, wrapper)
}
CompressionScheme::Bzip2 => {
let decoder = BzDecoder::new(file);
let reader = BufReader::new(decoder);
self.lookup_from_reader(reader, wrapper)
}
CompressionScheme::Xz => {
let decoder = XzDecoder::new(file);
let reader = BufReader::new(decoder);
self.lookup_from_reader(reader, wrapper)
}
CompressionScheme::Uncompressed => {
let reader = BufReader::new(file);
self.lookup_from_reader(reader, wrapper)
}
}
}
That's a bit tricky; the simplest solution is dynamic dispatch, but someone else may have a clever static-dispatch solution to offer. (The + '_ annotation on dyn is to allow temporary references. If you don't specify a lifetime, it defaults to 'static)
///Just a wrapper function for a file.
pub fn lookup<F>(&mut self, wrapper: &mut Wrapper, from_reader: F) -> Result<Vec<ChildData>, AppError>
where F: for<'a> FnOnce(&mut dyn BufRead+'a, &mut Wrapper)->Result<Vec<ChildData>, AppError> {
// open target file
let file = File::open(&self.path)?;
// if file is compressed, we need to call a specific reader
// create a specific reader for each compression scheme
match self.compression {
CompressionScheme::Gzip => {
let decoder = GzDecoder::new(file);
let reader = BufReader::new(decoder);
from_reader(&mut reader, wrapper)
}
CompressionScheme::Bzip2 => {
let decoder = BzDecoder::new(file);
let reader = BufReader::new(decoder);
from_reader(&mut reader, wrapper)
}
CompressionScheme::Xz => {
let decoder = XzDecoder::new(file);
let reader = BufReader::new(decoder);
from_reader(&mut reader, wrapper)
}
CompressionScheme::Uncompressed => {
let reader = BufReader::new(file);
from_reader(&mut reader, wrapper)
}
}
}
I tried another solution with a generic trait with no success either (I'll create a topic soon). I succeded using an Enum by I don't like too much this solution.