How to impl `Deref` to Target with Self lifetime?

I am the user of yoke crate,
in order to remove the lifetime in struct declaration.

But the API is not easy to use because it lacks implementation of Deref trait.
So I wrap it inside a new type, and want to implement Deref trait through it.

My example does not involve yoke for simplicity:

use core::ops::Deref;

struct Foo<'a> {
    data: &'a str,
}

struct Bar(Foo<'static>);

impl Deref for Bar {
    type Target = Foo<'static>;
    
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

fn main() {
    
}

The above code works,
but is there a way to specify Self lifetime in Target

The code below does not work

impl Deref for Bar {
    type Target = Foo<'Self>;
    
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

Thanks

Real example with yoke crate

/*
[dependencies]
yoke = { version = "0.7", features = ["derive"] }
*/

use core::ops::Deref;
use std::sync::Arc;
use yoke::{Yoke, Yokeable};

#[derive(Yokeable)]
struct Foo<'a> {
    data: &'a str,
}

struct Bar(Yoke<Foo<'static>, Arc<String>>);

impl Deref for Bar {
    // HELP: How to change this to `Self` lifetime?
    type Target = Foo<'static>;
    
    fn deref(&self) -> &Self::Target {
        // ERROR: Cannot compile here
        self.0.get()
    }
}

fn main() {
}

You can ellide the lifetime and let the compiler infer it for you:

type Target = Foo<'_>;

No, that's not possible in associated type position, for the same reason you have to be explicit about the lifetimes in the definition of a struct or enum. As impls are in global scope, there's no context anywhere for the compiler to infer any particular lifetime from, as there would be in a signature or a function body.

2 Likes

To answer the question: your problem is that there's no concrete lifetime to put in the associated Target type of Deref, and Deref::Target is not a GAT (generic associated type).

You thus have to be explicit about there being a reference and a named lifetime to begin with:


struct Bar<'a>(&'a Yoke<Foo<'static>, Arc<String>>);

impl<'a> Deref for Bar<'a> {
    type Target = Foo<'a>;
    
    fn deref(&self) -> &Self::Target {
        self.0.get()
    }
}

Rust Explorer


As a side note, don't use Arc<String> – it's an unnecessary double indirection. Use Arc<str> instead, like this.

The entire purpose of using yoke, is to remove the lifetime in Bar
So I need struct Bar, not struct Bar<'a>

This does not compile

The purpose of Yoke is that you can have a self-referential type :slight_smile: Anyway – if you need Bar to be non-generic, then introduce a second level of wrapper that you only create temporarily, when and where needed:

struct Bar(Yoke<Foo<'static>, Arc<String>>);
struct BarRef<'a>(&'a Bar);

impl<'a> Deref for BarRef<'a> {
    type Target = Foo<'a>;
    
    fn deref(&self) -> &Self::Target {
        self.0.0.get()
    }
}
1 Like

Sorry, I'm on the phone and didn't have time to check it.

Glad that @H2CO3 was here :sweat_smile:

Guess why Yoke itself doesn't implement Deref.. because the target type (as returned by the Yoke::get method) would need to depend on the lifetime of its &self argument! It's the same problem :grin:

Wrapping a Yoke type won't make you magically be able to achieve what yoke itself couldn't :wink:

7 Likes

Thanks,
I think the best solution is to not use Yoke wrapped type in most situation.
The alternative may be to delegate all APIs of borrowed type to owned type.