使用 AsRef 返回包含在输入包装器类型中的引用

Returning reference contained in an input wrapper type using AsRef

我有一个围绕 &str 的包装器来保持不变性。当我尝试 return 一个新的包装器时,它基本上将参数包装的相同数据包装到一个函数中 (playground),我收到“无法 return 值引用函数参数 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..],
    }
}

索引与错误无关,但当我使用 s.raw 而不是通过 as_ref() 直接访问成员时它起作用了。有没有什么好的方法可以避免 rawmy_function 可见?

如果主要要求不是使内部 raw 字段对 my_function 可见,并且您可以在包装内部 [=15= 之前使用原始 MyStrWrapper 实例] 在一个新实例中,那么我建议使用 From 特性在 MyStrWrapper&str 之间进行转换,并在 my_function 的实现中使用它。示例:

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


如果你真的想从 my_function 中隐藏 raw 你也可以在另一个方向上实现转换,&strMyStrWrapper 使用 TryFrom 特征,因为如果不满足不变量,转换可能会失败。更新示例:

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

您可以将 s 作为具有相同生命周期 (Playground) 的参考传递:

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

然后参考调用:

my_function(&s);