nnnmmm
nnnmmm

Reputation: 8764

Returning reference contained in an input wrapper type using AsRef

I have a wrapper around a &str to maintain an invariant. When I try to return a new wrapper that basically wraps the same data wrapped by the argument to a function (playground), I'm getting "cannot return value referencing function parameter s"

struct MyStrWrapper<'a> {
    raw: &'a str,
}

impl AsRef<str> for MyStrWrapper<'_> {
    fn as_ref(&self) -> &str {
        &self.raw
    }
}

fn my_function<'inp>(s: MyStrWrapper<'inp>) -> MyStrWrapper<'inp> {
    MyStrWrapper {
        raw: &s.as_ref()[1..],
    }
}

The indexing is unrelated to the error, but it works when I directly access the member with s.raw instead of via as_ref(). Is there a good way around this that doesn't make raw visible to my_function?

Upvotes: 3

Views: 561

Answers (2)

C14L
C14L

Reputation: 12558

You could pass s as a reference with the same lifetime (Playground):

fn my_function<'inp>(s: &'inp MyStrWrapper<'inp>) -> MyStrWrapper<'inp> 

And then call with reference:

my_function(&s);

Upvotes: 2

pretzelhammer
pretzelhammer

Reputation: 15135

If the main requirement is not making the inner raw field visible to my_function and you're okay with consuming the original MyStrWrapper instance before wrapping the inner &str in a new instance then I would suggest using the From trait to convert between MyStrWrapper and &str and use that within your implementation of my_function. Example:

struct MyStrWrapper<'a> {
    raw: &'a str,
}

impl<'a> From<MyStrWrapper<'a>> for &'a str {
    fn from(item: MyStrWrapper<'a>) -> Self {
        item.raw
    }
}

fn my_function<'inp>(s: MyStrWrapper<'inp>) -> MyStrWrapper<'inp> {
    // convert MyStrWrapper into &str without exposing "raw" field to function
    let inner_str = s.into();
    // do whatever you need to do with inner_str here
    // re-wrap inner_str here
    MyStrWrapper {
        raw: inner_str,
    }
}

fn main() {
    let s = MyStrWrapper {
        raw: "Hello, world!",
    };
    my_function(s);
}

playground


If you really want to hide raw from my_function you can also implement the conversion in the other direction, &str to MyStrWrapper using the TryFrom trait since it's possible for the conversion to fail if the invariant isn't met. Updated example:

use std::convert::TryInto;
use std::convert::TryFrom;

struct MyStrWrapper<'a> {
    raw: &'a str,
}

impl<'a> From<MyStrWrapper<'a>> for &'a str {
    fn from(item: MyStrWrapper<'a>) -> Self {
        item.raw
    }
}

#[derive(Debug)]
enum MyInvariant {
    FailureReason
}

impl<'a> TryFrom<&'a str> for MyStrWrapper<'a> {
    type Error = MyInvariant;

    fn try_from(item: &'a str) -> Result<Self, Self::Error> {
        // check invariant on item
        // if fails return Err(MyInvariant::FailureReason)
        // else
        Ok(MyStrWrapper {
            raw: item
        })
    }
}

fn my_function<'inp>(s: MyStrWrapper<'inp>) -> MyStrWrapper<'inp> {
    // convert MyStrWrapper into &str without exposing "raw" field to function
    let inner_str: &str = s.into();
    // do whatever you need to do with inner_str here
    // re-wrap inner_str here
    inner_str.try_into().unwrap()
}

fn main() {
    let s = MyStrWrapper {
        raw: "Hello, world!",
    };
    my_function(s);
}

playground

Upvotes: 2

Related Questions