Is `Borrow<T>` designed to only be implemented by unsized `T`s?

Let's say I have these two structs:

struct Owned<T>(T);

struct Borrowed<'a, T>(&'a T);

I feel like I should be able to implement ToOwned for Borrowed when T is Clone to get an Owned:

impl<'a, T: Clone> ToOwned for Borrowed<'a, T> {
    type Owned = Owned<T>;
    
    fn to_owned(&self) -> Self::Owned {
        Owned(self.0.clone())
    }
}

however ToOwned::Owned has a Borrow<Self> bound, so Owned<T> needs to implement Borrow<Borrowed<'a, T>> for this to work:

use std::borrow::Borrow;

impl<'a, T> Borrow<Borrowed<'a, T>> for Owned<T> {
    fn borrow(&self) -> &Borrowed<'a, T> {
        // &Borrowed(&self.0)
        todo!()
    }
}

which doesn't work because Borrowed(&self.0) goes out of scope when borrow() ends.

Looking around in std, Borrow<T> seems to always be implemented for !Sized Ts, e.g. Borrow<str> for String, Borrow<[T]> for Vec<T>, Borrow<Path> for PathBuf, Borrow<CStr> for CString, etc.

Should I just add a fn to_owned(&self) -> Owned<T> in Borrowed's impl block?

Link to the playground.

This hasn't got much to do with Sized. The problem is that Borrow::borrow() returns a reference, so you can only ever return a reference from it, and not a wrapper type around a reference.

That blanket impl already exists (and it's possible because T: Borrow<T>).

1 Like

This hasn't got much to do with Sized . The problem is that Borrow::borrow() returns a reference

Yes, I was asking if Borrow::borrow() returns a reference because it's designed to only be implemented by !Sized types which have to be behind some sort of reference like &, Box, Arc, etc.

That blanket impl already exists

I'm not sure how that applies to this example :thinking:. The goal was to implement ToOwned for Borrowed, not for T.

No, it's more of an omission/oversight. It would definitely be useful if Borrow::borrow() could return a wrapper around references.

The same thing applies to Index::index(), which also returns a reference, so that the desugaring of x[i] to *x.index(i) can be an lvalue (place expression). However, this also makes it impossible to return general non-reference types from an indexing operation.

I was trying to draw a parallel to show that Clone implying ToOwned does make sense. However, I'm afraid that with the current signature, it's impossible to safely implement it for your Borrowed type.

There is an unsafe solution: make Borrowed hold a T directly, and make it #[repr(transparent)], then you can cast between the appropriate pointer types:

struct Owned<T>(T);

#[repr(transparent)]
struct Borrowed<T>(T);

impl<T: Clone> ToOwned for Borrowed<T> {
    type Owned = Owned<T>;
    
    fn to_owned(&self) -> Self::Owned {
        Owned(self.0.clone())
    }
}


impl<T> Borrow<Borrowed<T>> for Owned<T> {
    fn borrow(&self) -> &Borrowed<T> {
        unsafe {
            &*(&self.0 as *const T).cast()
        }
    }
}
1 Like

There is an unsafe solution: make Borrowed hold a T directly

I'm open to using unsafe but Borrowed has to hold a reference to a T, otherwise it's not really borrowing anything and I wouldn't need to define it, I could just use Owned and implement Clone for it.

You just need to use #[repr(transparent) for both structs.

Check with Miri, but I think #[repr(transparent) must guarantee safety of the following:

#[repr(transparent)]
struct Owned<T>(T);

#[repr(transparent)]
struct Borrowed<'a, T>(&'a T);

impl<'a, T: Clone> ToOwned for Borrowed<'a, T> {
    type Owned = Owned<T>;
    
    fn to_owned(&self) -> Self::Owned {
        Owned(self.0.clone())
    }
}

impl<'a, T> core::borrow::Borrow<Borrowed<'a, T>> for Owned<T> {
    fn borrow(&self) -> &Borrowed<'a, T> {
        unsafe { std::mem::transmute::<&Self, &Borrowed<'a, T>>(&self) }
    }
}

No, it doesn't. Please take some time to understand my solution, it's correct. (You can get a &T from Borrowed<T> by writing &borrowed.0.)

That's certainly incorrect. self is a function argument, so it's a variable local to the function. The expression &self is thus a dangling pointer immediately after the function returns. This dumps core, and Miri complains about the dangling pointer too.

You are right. It really looks as if it's impossible if Borrowed contains reference, but from what I understand that's really what was asked about: I have reference in a data structure Borrowed and want to clone object and to to owner value with to_owned.

In your non-solution Borrowed no longer includes reference, it has full object inside, thus there are no need to clone anything, you can just take that object and use it.

It's easy to create to_owned function and it makes sense, but then everything falls apart at attempt to create borrow

No, it doesn't. Please take some time to understand my solution

I may be missing something but I don't see how having Borrowed hold an owned T is useful in this case. I'm not sure if it's clear but I need Borrowed to hold a &'a T in my actual code, I can't change that.

You can get a &T from Borrowed<T> by writing &borrowed.0 .

I can also get a &T from a Owned<T> in the exact same way.

Indeed, that wasn't clear. In this case, you are unfortunately out of luck and won't be able to implement Borrow.

The important thing is not that they are !Sized, but that they are owning. Note that str, [T], PathBuf, Rc<T> and other such types own the information they carry. Rc<T> is Sized, e.g.

But they are used, often, on references. And when you call to_owned you turn &str into String, &[T] becomes Vec<T> and so on.

That's most definitely not what you are trying to achieve. Your Owned type does own the data, but your Borrowed doesn't.

That's why you get that mismatch.

I guess the names of traits led you on this wild goose chase.

The Borrow trait allow you to borrow a value in some type that may or may not be the original type, but only borrow.
Trivially you can borrow any type from itself, meaning you can get &T from any T.

Implementing Borrow<T> will only ever give you a &T, it might be that there is no actual instance of T at any moment.
For example, look at String. You can borrow a str from a String, so you can get a &str, not a str. But if you look into String, it doesn't hold a str, it holds a Vec<u8>. You can get a reference to the data interpreted in a different way, but you can't get an owned copy of that data, not with the Borrow trait.

A value of type Borrowed<'a, T> is not borrowed, it is an owned structure of type Borrowed and that structure hold a reference borrowing a type T. So you can't return a Borrowed<'a, T> from the Borrow trait, only a &Borrowed<'a, T>.

You can get a &T from Borrowed<T> by writing &borrowed.0 .

I can also get a &T from a Owned<T> in the exact same way.

It is not in the "exact same way" because it is not a Borrowed<T> its a &Borrowed<T>
So it has to go through an extra indirection and has to comply with an extra lifetime.


What you are doing seems a lot more like it relates to Cow than with Borrow

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.