Reputation: 8764
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
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
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);
}
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);
}
Upvotes: 2