Search code examples
genericsrusttraits

How can I write a generic method in Rust that can accept any value that can be converted into another?


I want to be able to receive a path-like value in a generic function. The idea would be that I want to be able to receive anything that could be converted into PathBuf, like this:

fn build<P>(path: P) -> Some<Handler>{
  let mut base_path = PathBuf::from(path);

  ...
}

But, of course, this doesn't work without the proper trait. Yet I'm unable to figure out how to write the generic trait stating that I want to accept a value that can be accepted as from for another type. Like this (which of course doesn't work):

fn build<P>(path: P) -> Some<Handler>
  where P: PathBuf::from::<P>
{
  let mut base_path = PathBuf::from(path);

  ...
}

Solution

  • The proper trait for cases where you want "anything that converts to" is Into.

    However, the idiomatic trait for accepting paths in particular is AsRef<Path>. This is how the standard library functions taking a path do it.

    fn build<P>(path: P) where P: AsRef<Path>
    

    or the shorter form:

    fn build<P: AsRef<Path>>(path: P)
    

    or even shorter:

    fn build(path: impl AsRef<Path>)
    

    If your function is not trivial, you should immediately forward to a non-generic function:

    fn build(path: impl AsRef<Path>) {
      build_impl(path.as_ref())
    }
    fn build_impl(path: &Path) { .... }
    

    And if you need a PathBuf, you simply call to_owned() on the Path.

    For reference, you use the same technique with Into:

    fn build(p: impl Into<PathBuf>) { build_impl(p.into()) }
    fn build_impl(p: PathBuf) {}