… but it doesn't! So I had to make some changes:
-impl<T, U> AsRef<U> for Owned<T>
-where
- T: AsRef<U>,
- U: ?Sized,
-{
- fn as_ref(&self) -> &U {
- self.0.as_ref()
+impl<T> AsRef<T> for Owned<T> {
+ fn as_ref(&self) -> &T {
+ &self.0
}
}
This is such that the following tests run fine:
#[test]
fn test_vec_as_ref() {
let wrapped: Owned<Vec<i32>> = Owned(vec![2, 7, 4]);
let vec_ref: &Vec<i32> = wrapped.as_ref();
assert_eq!(vec_ref, &vec![2, 7, 4]);
let slice_ref: &[i32] = wrapped.as_ref();
assert_eq!(slice_ref, &[2, 7, 4] as &[i32]);
}
#[test]
fn test_int_as_ref() {
let wrapped: Owned<i32> = Owned(5);
let reference: &i32 = wrapped.as_ref();
assert_eq!(reference, &5);
}
The new implementation of AsRef<T> for Owned<T>
is akin to impl<'_, T> AsRef<T> for Cow<'_, T>
. But note how there is also impl AsRef<Path> for Cow<'_, OsStr>
.
The more I dig into this, the uglier it gets!
Update:
I decided it's best to revert the above diff, i.e. to keep:
impl<T, U> AsRef<U> for Owned<T>
where
T: AsRef<U>,
U: ?Sized,
{
fn as_ref(&self) -> &U {
self.0.as_ref()
}
}
This means you cannot (generally) use .as_ref()
to go from Owned<T>
to T
. But implementation of AsRef<U>
for Owned<T>
(where T
implements AsRef<U>
) is needed to be able to use .as_ref()
for a "cheap reference-to-reference conversion" (which is what AsRef
is provided for).
In that sense, I believe that impl<'_, T> AsRef<T> for Cow<'_, T>
in std
is wrong! It should be impl<T: AsRef<U>, U: ?Sized> AsRef<U> for Cow<'_, T>
impl<T: ?Sized + ToOwned + AsRef<U>, U: ?Sized> AsRef<U> for Cow<'_, T>
instead. However, then .as_ref()
cannot be used to go from Cow<'_, T>
to &T
anymore. But .as_ref()
cannot be used to go from T
to &T
either. So that'd be just consistent! .borrow()
can be used for that.
I think it's too late though to fix std
.
We often wrongly use impl AsRef<Path>
, where, in-fact, we mean B: Borrow<P> where P: ?Sized + AsRef<Path>
.
Demonstration:
use std::borrow::{Borrow, Cow};
use std::ffi::{OsString, OsStr};
use std::path::{Path, PathBuf};
#[derive(Clone)]
struct CurDir;
impl AsRef<Path> for CurDir {
fn as_ref(&self) -> &Path {
".".as_ref()
}
}
fn foo(_: impl AsRef<Path>) {}
fn bar<P: ?Sized + AsRef<Path>, B: Borrow<P>>(_: B) {}
fn main() {
foo(OsString::from("."));
foo(&OsString::from(".") as &OsString);
foo(&OsString::from(".") as &OsStr);
foo(PathBuf::from("."));
foo(&PathBuf::from(".") as &PathBuf);
foo(&PathBuf::from(".") as &Path);
foo(CurDir);
foo(&CurDir);
// foo(Cow::<'_, OsString>::Owned(OsString::from(".")));
// foo(Cow::<'_, OsString>::Borrowed(&OsString::from(".")));
foo(Cow::<'_, OsStr>::Borrowed(&OsString::from(".") as &OsStr));
// foo(Cow::<'_, PathBuf>::Owned(PathBuf::from(".")));
// foo(Cow::<'_, PathBuf>::Borrowed(&PathBuf::from(".")));
foo(Cow::<'_, Path>::Borrowed(&PathBuf::from(".") as &Path));
// foo(Cow::<'_, CurDir>::Owned(CurDir));
// foo(Cow::<'_, CurDir>::Borrowed(&CurDir));
bar::<OsString, _>(OsString::from("."));
bar::<OsString, _>(&OsString::from(".") as &OsString);
bar::<OsStr, _>(&OsString::from(".") as &OsStr);
bar::<PathBuf, _>(PathBuf::from("."));
bar::<PathBuf, _>(&PathBuf::from("."));
bar::<Path, _>(&PathBuf::from(".") as &Path);
bar::<CurDir, _>(CurDir);
bar::<CurDir, _>(&CurDir);
bar::<OsString, _>(Cow::<'_, OsString>::Owned(OsString::from(".")));
bar::<OsString, _>(Cow::<'_, OsString>::Borrowed(&OsString::from(".")));
bar::<OsStr, _>(Cow::<'_, OsStr>::Borrowed(&OsString::from(".") as &OsStr));
bar::<PathBuf, _>(Cow::<'_, PathBuf>::Owned(PathBuf::from(".")));
bar::<PathBuf, _>(Cow::<'_, PathBuf>::Borrowed(&PathBuf::from(".")));
bar::<Path, _>(Cow::<'_, Path>::Borrowed(&PathBuf::from(".") as &Path));
bar::<CurDir, _>(Cow::<'_, CurDir>::Owned(CurDir));
bar::<CurDir, _>(Cow::<'_, CurDir>::Borrowed(&CurDir));
}
Unfortunately, type inference chokes on that.
I believe all this could be solved if there was a generic impl<T: ?Sized> AsRef<T> for T
, I believe. Which cannot exist, unfortunately.
Sorry for the incremental updates.
Actually using impl AsRef<Path>
would be okay if there was impl<T: ?Sized + ToOwned + AsRef<U>, U: ?Sized> AsRef<U> for Cow<'_, T>
in std
, because impl AsRef<Path> for Path
. But …
- This doesn't hold in the general case of
impl AsRef<T>
. - We have no
impl<T: ?Sized + ToOwned + AsRef<U>, U: ?Sized> AsRef<U> for Cow<'_, T>
instd
.