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.

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.