Type inference of generic parameters

⚓ Rust    📅 2026-04-16    👤 surdeus    👁️ 3      

surdeus

I'm currently working on a simple implementation of grep to get to know the language. However, when introducing some generics, the compiler throws an error about it not being able to infer the type of a generic parameter. Here's a minimum working example without any of the real functionality:

use ignore::WalkBuilder;
use std::path::{Path, PathBuf};
use std::io;
use std::fs::File;
use clap::Parser;

mod parser {
    use super::*;

    #[derive(Parser)]
    #[command(name = "grep", version, about, long_about = None)]
    pub struct Grep {
        /// Path/paths to search for expression
        pub path: PathBuf,
    
        /// Search recursively in current directory
        #[arg(short, long, default_value_t = false)]
        pub recursive: bool,
    }
}

fn main() -> io::Result<()> {
    let args = parser::Grep::parse();
    find_matches(args.recursive, &args.path, &args, find_match_in_file)?;
    Ok(())
}

fn find_matches<F, P, S>(recursive: bool, path: P, args: &parser::Grep, cb: F) -> io::Result<()>
where P: AsRef<Path>,
      F: Fn(S, &parser::Grep) -> io::Result<()>,
      S: AsRef<Path>,
{
    let walker = if recursive {
        WalkBuilder::new(path).max_depth(None).build()
    } else {
        WalkBuilder::new(path).max_depth(Some(1)).build()
    };

    for entry in walker {
        let entry = match entry {
            Ok(entry) => entry,
            Err(_error) => {
                eprintln!("Skipping path");
                continue;
            }
        };
        let p = entry.path();

        if p.is_file() {
            println!("Scanning file: {}", p.to_str().unwrap_or("Unknown file"));
            match cb(p, args) {
                Ok(()) => { /* Do nothing */ },
                Err(error) => eprintln!("Couldn't search file: {:?}", error),
            }
        }
    }
    Ok(())
}

fn find_match_in_file<S>(path: S, _args: &parser::Grep) -> io::Result<()>
where S: AsRef<Path>,
{
    let _file = File::open(path)?;
    Ok(())
}

I get the following error when compiling:

error[E0283]: type annotations needed
  --> src/bin/test-clap.rs:24:53
   |
24 |     find_matches(args.recursive, &args.path, &args, find_match_in_file)?;
   |                                                     ^^^^^^^^^^^^^^^^^^ cannot infer type of the type parameter `S` declared on the function `find_match_in_file`
   |
   = note: multiple `impl`s satisfying `_: AsRef<Path>` found in the following crates: `clap_builder`, `std`:
           - impl AsRef<Path> for OsString;
           - impl AsRef<Path> for Path;
           - impl AsRef<Path> for Str;
           - impl AsRef<Path> for clap::builder::OsStr;
           - impl AsRef<Path> for std::ffi::OsStr;
           - impl AsRef<Path> for std::path::PathBuf;
           - impl AsRef<Path> for std::string::String;
           - impl AsRef<Path> for str;
note: required by a bound in `find_matches`
  --> src/bin/test-clap.rs:31:10
   |
28 | fn find_matches<F, P, S>(recursive: bool, path: P, args: &parser::Grep, cb: F) -> io::Result<()>
   |    ------------ required by a bound in this function
...
31 |       S: AsRef<Path>,
   |          ^^^^^^^^^^^ required by this bound in `find_matches`
help: consider specifying the generic argument
   |
24 |     find_matches(args.recursive, &args.path, &args, find_match_in_file::<S>)?;
   |                                                                       +++++                                                                                        +++++  

According to the docs for the ignore crate, and it's structs, iterating over ignore::Walk should yield ignore::DirEntry, and calling .path() on those should return &Path. So why can't rust infer that the type parameter S is &Path?

3 posts - 2 participants

Read full topic

🏷️ Rust_feed