Accept Trait by either value or reference


#1

As shown in the code below, I’d like to make a generic function for some trait Foo, which can recieve both &String or &str (and also &PathBuf or &Path).

use std::path::Path;

trait Foo { }

impl Foo for str { }
impl Foo for Path { }

fn foo<F: Foo + ?Sized>(_f: &F) {
    println!("foo!");
} 

fn main() {
    let s = String::from("Hello, World");
    
    foo(&s); // - This line doesn't compile
    foo("Hello, World?");
}

(Playground)

One way would be to add the following impls too:

impl Foo for String { };
impl Foo for PathBuf { };

However what would be even better is if I can somehow make it work so that anything which implements AsRef<str> or AsRef<Path> can be accepted.

What is considered the idiomatic approach here?

Thanks
David


#2

hmm i think you can do something like

impl<T> Foo for T where T: ?Sized + AsRef<Path> {}

playground


#3

Oh nice, though it doesn’t work if I try both of these together (complains of overlapping impls):

impl<T> Foo for T where T: ?Sized + AsRef<Path> {}
impl<T> Foo for T where T: ?Sized + AsRef<str> {}

#4

Oh right they are conflicting since T could implement both AsRef<Path> and AsRef<str> and then the compiler don’t knows with impl block to use for that T.
idk if there is a way around it sry.