How can I return reference of the struct field?

I am implementing Deref for a tuple struct in my project which has a String in it.
When dereferenced I want to return a &str from that String but I can't figure out how I can do that.

It would look something like this:

use std::ops::{Deref, DerefMut};

struct Foo(String);

impl Deref for Foo {
    type Target = str;
    fn deref(&self) -> &str {
        &self.0
    }
}

impl DerefMut for Foo {
    fn deref_mut(&mut self) -> &mut str {
        &mut self.0
    }
}

Sorry for temporarily having duplicated code; I accidentally pressed post, and this mobile keyboard is not meant to write code.

And how bout the lifetime of the self.0 how is it possible in this example to express to the compiler that it lives after the defer function returns ?

You could annotate them, but the compiler does this automatically:

use std::ops::{Deref, DerefMut};

struct Foo(String);

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

impl DerefMut for Foo {
    fn deref_mut<'a>(&'a mut self) -> &'a mut str {
        &mut self.0
    }
}

The more normal way would be to leave them as anonymous lifetimes since you're not doing anything with the lifetimes that would require annotation:

use std::ops::{Deref, DerefMut};

struct Foo(String);

impl Deref for Foo {
    type Target = str;
    fn deref(&'_ self) -> &'_ str {
        &self.0
    }
}

impl DerefMut for Foo {
    fn deref_mut(&'_ mut self) -> &'_ mut str {
        &mut self.0
    }
}
2 Likes

Oh i see this makes a lot of sense thanks.
Lastly what would be the difference if type Target=String and ->&String was used instead of str?

If we wrote:

type Target = String;

Then our function signature would look like this:

fn deref(&'_ self) -> &'_ String;

Which breaks the guideline that you should usually replace &String with &str. But this also makes it so that you can now mutate the String, in the DerefMut impl you need to have the same Target as Deref so, this would become

fn deref_mut(&'_ mut self) -> &'_ mut String;

With

type Target = &String;

You would have a compiler error since the type needs to have a lifetime attached to the reference, which could only be acquired from your struct, but your struct contains no lifetimes so this would be erroneous.

How can you mutate a &String from deref its an immutable ref ?

From your previous post, the first suggestion to change the type to String? Well I only showed the signature for the deref, deref_mut would be changed to this:

impl DerefMut for Foo {
    fn deref_mut(&'_ mut self) -> &'_ mut String {
        &mut self.0
    }
}

If you're talking about how a String becomes &mut str then just how a String can coerce into &str it can coerce into &mut str.

I see thank you.

Now i noticed that If my target is a str then *val would result in a str which cannot be used without a pointer so I need to do &*val is there a nicer way do do this without awkward &* in front of every value?
What I would like to do is simply &val or *val only.

This is solved by automatic deref coercion:

let x = String::from("Abc");
let y = &x; //y: &String
let z: &str = &x; //x: &str

What this shows is that the compiler will automatically produce &U where T: Deref<Target = U> and &U is needed, otherwise it will provide &T. Notice how on my examples for implementing the Deref trait, I show it like so:

impl Deref for Foo {
    type Target = str;
    fn deref(&'_ self) -> &'_ str {
        &self.0
    }
}

&self.0 would evaluate to &String, but since the compiler sees I need &str and String: Deref<Target = str> it works.


Take a look at
https://doc.rust-lang.org/book/ch15-02-deref.html

In my case I am trying to print the internal value so i have a println!("{}",*val) and it gives me the error saying size of str cannot be known at compile time

Then unfortunately all I can suggest is to use &*val unless you are okay with #[derive(Debug)]-ing your struct.

1 Like

Thanks

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.