I created a Playground example to demonstrate how that could look like (without specialization or any Nightly features, just using stable Rust):
// Same as `Deref` but duplicated to allow demonstration here
trait MyDeref {
/* … */
}
/* … */
// Same as `AsRef` but with different blanket implementation
trait MyAsRef<T: ?Sized> {
fn my_as_ref(&self) -> &T;
}
impl<T, U> MyAsRef<U> for T
where
T: ?Sized + MyDeref,
U: ?Sized,
<T as MyDeref>::Target: MyAsRef<U>,
{
fn my_as_ref(&self) -> &U {
self.my_deref().my_as_ref()
}
}
// Manual (trivial) implementation needed
// (would conflict with `MyDeref` if also implemented for `str`)
impl MyAsRef<str> for str {
fn my_as_ref(&self) -> &str {
self
}
}
// Manual (trivial) implementation needed
// (would conflict with `MyDeref` if also implemented for `Path`)
impl MyAsRef<Path> for Path {
fn my_as_ref(&self) -> &Path {
self
}
}
// Cheap conversion from `&str` into `&Path`
impl MyAsRef<Path> for str {
fn my_as_ref(&self) -> &Path {
Path::new(self)
}
}
fn takes_path(_: impl MyAsRef<Path>) {}
fn main() {
takes_path("Hello".to_string());
takes_path(&"Hello".to_string() as &String);
takes_path(&&&&&"Hello".to_string() as &&&&&String);
takes_path("Hello");
takes_path(&&"Hello");
takes_path(Path::new("Hello"));
takes_path(&&&Path::new("Hello"));
takes_path(Cow::Borrowed(&"Hello"));
takes_path(&&&&Cow::Borrowed(&&&&&&&&&"Hello"));
takes_path(&&&&Cow::Borrowed(&&&&&&&&&Path::new("Hello")));
takes_path(Box::new("Hello".to_string()));
takes_path("Hello".to_string().into_boxed_str());
takes_path(Box::new(Cow::Borrowed(&&&"Hello")));
}
Note that the main()
function compiles properly here, opposed to the broken examples in my OP.
(edit: moved implementation of MyDeref for String
up and removed wrong comment)