Impl Trait, Deref, and AsRef


#1

I’m experimenting with heavily using impl Trait, and today I got the following error:

The trait bound

impl Deref<Target = Path>: std::convert::AsRef<std::path::Path>

is not satisfied.

Is it possible to do

impl<T> Deref for AsRef<T> {
    type Target = T;
    fn deref(&self) -> &Self::Target {
        self.as_ref()
    }
}

?

EDIT I should say that doing &mytype.deref() means I don’t need this impl, but I can’t think of any reasons this couldn’t be implemented.


#2

Maybe you should be using impl Deref<Target = Path> + AsRef<Path>.


#3

I just can’t think of a reason why Deref<Target=T> isn’t implemented for AsRef


#4

Isn’t the error message saying the opposite? You have a type impl Deref<Target = Path>, and passing it somewhere that requires the bound AsRef<Path>. Here’s an example playground, you can fix the error by adding a &* in front of the foo() call.

I think that implies there needs to be

impl<T, U> AsRef<T> for U where U: Deref<Target = T> {
    fn as_ref(&self) -> &T {
        &**self
    }
}

which definitely looks likely to cause coherence issues.


#5

The String type has the following implementations for AsRef:

impl AsRef<[u8]> for String { ... }
impl AsRef<str> for String { ... }
impl AsRef<OsStr> for String { ... }
impl AsRef<Path> for String { ... }

Which of these, in your opinion, should String deref to? Note that, because Deref::Output is an associated type, there can be only one Deref.


#6

The problem is I have a function like the following

struct MyWrapper(RefCell<String>);

impl MyWrapper {
    /// A function which read-borrows the ref-cell without leaking the `Ref` type 
    /// (this should be an implementation detail). 
    fn get_a_string_ref(&self) -> AsRef<str> {
        self.borrow()
    }
}

@troiganto: I want it the other way round, Deref -> AsRef.

EDIT my motivation is to allow myself to switch to Mutex, or RwLock etc. making “MyWrapper” Sync, without it being a breaking change.


#7

Perhaps something like this:

struct DerefAsRef<D: Deref>(D);

impl<D: Deref> AsRef<D::Target> for DerefAsRef<D> {
    fn as_ref(&self) -> &D::Target {
        self.0.deref()
    }
}

pub struct MyWrapper(RefCell<String>);

impl MyWrapper {
    /// A function which read-borrows the ref-cell without leaking the `Ref` type 
    /// (this should be an implementation detail). 
    pub fn get_a_string_ref<'a>(&'a self) -> impl AsRef<String> + 'a {
        DerefAsRef(self.0.borrow())
    }
}

You can, e.g., switch in a Mutex and then it’ll be DerefAsRef(self.0.lock().unwrap()). DerefAsRef stays private and hidden behind impl Trait.


#8

This is good! I was wondering how I like the lifetime of the ref to the lifetime of self.


#9

Is there a way to do this twice, say if you have

pub struct MyStruct {
    // some fields
    field: RefCell<HashMap<String, String>>
}

pub struct MyWrapper(RefCell<MyStruct>);

impl MyWrapper {
    pub fn get_a_string_ref<'a>(&'a self, key: String) -> impl AsRef<String> + 'a {
   // ???
}

?

What I’m trying to do is allow for complex ownership, but where everything is cleaned up when MyWrapper goes out of scope (no leaks, since MyWrapper owns everything). If I used Rc I could do what I want more easily, buit at the risk of leaks.


#10

I would consider inverting how a caller gets access to the reference, e.g.:

pub struct MyStruct {
    // some fields
    field: RefCell<HashMap<String, String>>,
}

pub struct MyWrapper(RefCell<MyStruct>);

impl MyWrapper {
    pub fn with_val<F>(&self, key: &str, f: F)
    where
        F: FnOnce(&str),
    {
        let s = self.0.borrow();
        let m = s.field.borrow();
        // give them temp access here
        m.get(key).map(|k| f(k));
    }
}

#11

That’s a good idea.

I’d prefer the original solution though as it would give the user more flexibility. Is there a way to get the following playground working:

use std::ops::Deref;

pub struct DerefDerefAsRef<D>(pub D);

impl<D, D2> AsRef<D2::Target> for DerefDerefAsRef<D>
where
    D: Deref<Target=D2>,
    D2: Deref
{
    fn as_ref(&self) -> &D2::Target {
        self.0.deref().deref()
    }
}

fn main() {
    let val = DerefDerefAsRef(Box::new(Box::new(42u8)));
    assert_eq!(val.as_ref(), &42u8);
}

#12

For this example you just need to add the 'static bound on the inner type:

D2: Deref + 'static

But I don’t think this solution scales - what if you have 3 layers later? Etc

Why do you think this gives the user more flexibility?


#13

Maybe I should experiment with both approaches and see which one feels more natural.


#14

I’ve worked out that I can’t do what I want with Ref::map. This is my actual code

use std::cell::Ref;

Ref::map(thing.borrow(), |b| Ref::map(b.thing2.borrow(), |b2| b2.process())))

For what I want to do, I need the signature of Ref::map to be

pub fn map<U, F>(orig: Ref<'b, T>, f: F) -> Ref<'b, U> 
where for<'a>
    F: FnOnce(&'a T) -> R,
    R: AsRef<U> + 'a
    U: ?Sized,

This is beyond the limit of my understanding so I may have made a mistake.


#15

Yeah, I don’t think Ref::map() will work. Is this an attempt to make DerefDerefAsRef work with nested cells?

I still think you’ll have an easier time with the “caller passes you a closure” approach.

Also, have you thought about layouts that don’t involve nested cells?


#16

I think I agree with you at this point that the closure approach might be more sensible.

But I want to get this working, to help me understand the type system. Now I’m trying to write my own version of RefCell where the Ref is generic over the type of reference, but I’m struggling because &T doesn’t impl AsRef<T> :frowning: