Smart pointer which owns its target

… 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!

:confounded:


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.

:frowning_face:


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));
}

(Playground)


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> in std.

:crazy_face: