返回字符串如何修复无法返回引用临时值的值

问题描述 投票:0回答:2

我想在返回之前过滤

msg
函数中的
debug_args

pub trait MyLog {
    fn debug_args(&self) -> &dyn std::fmt::Debug;
}

pub struct TracingMyLog<'a> {
    record: &'a tracing::Event<'a>,
    fields: Map<String, Value>,
    message: Option<String>,
}

impl<'a> MyLog for TracingMyLog<'a> {
    fn debug_args(&self) -> &dyn std::fmt::Debug {
        if let Some(msg) = self.message.as_ref() {
            msg
        } else {
            &""
        }
    }
}

我想在 msg 上使用正则表达式,但收到此错误:

cannot return value referencing temporary value returns a value referencing data owned by the current function

fn regex_msg(str: &String) -> String {
    let re = Regex::new("sig: \"0x[a-fA-F0-9]+\"").unwrap();
    re.replace_all(&str, "sig: \"***filtered***\"")
        .to_string()
}

    fn debug_args(&self) -> &dyn std::fmt::Debug {
        if let Some(msg) = self.message.as_ref() {
            &regex_msg(msg)
        } else {
            &""
        }
    }

我知道这是试图返回对函数本地值的引用,一旦函数返回,其本地值就会被删除,对它们的任何引用都会悬空。

我还尝试在新函数中克隆

msg
字符串并返回对它的引用,这也是编译器不允许的。

最后,我认为

Cow<str>
存在一种解决方法,但我不想使用它。

问。如果可能的话,如何在不改变函数签名的情况下实现上述

msg
的过滤操作?

string logging rust lifetime
2个回答
0
投票

返回的

&str
总是有生命周期的。因此,如果您想返回引用您在函数中创建的数据的
&str
,通常有两种可能性:

  1. 将数据存储在
    self
    对象中,并返回生命周期为
    &self
    的引用。
  2. 泄露数据并返回
    &'static str

也许您可以在存储消息时应用正则表达式(假设

self.message
是私有字段)?

或者,作为主题的不同变体,您可以将

message
存储为
Option<String>
,而不是
Option<MySecretString>
,直接返回对此的引用,并将正则表达式处理放入
Debug
MySecretString
 impl 中
。我想这就是我会做的。


0
投票

就我个人而言,这就是我的处理方法:

如果您确实无法更改

debug_args
的签名,那么我会将过滤后的消息存储在结构体的
String
成员中,以便您可以返回对其的引用。然而,
MyLog
看起来像是你自己的特质,所以我真的建议你改变它。

我会将

MyLog
更改为如下所示:

pub trait MyLog<'a, T>
where
    T: std::fmt::Debug + 'a
{
    fn debug_args(&'a self) -> T;
}

这比返回

&dyn Trait
更惯用(它有它的位置,但从我所见这里没有必要)。这基本上是说“你可以实现它来返回任何类型,只要该类型实现
Debug
并且至少与你借用的
&self
一样长”。

然后你可以这样实现:

impl<'a, 'b> MyLog<'a, Cow<'a, str>> for TracingMyLog<'b>
{
    fn debug_args(&self) -> Cow<str> {
        if let Some(msg) = self.message.as_ref() {
            regex_msg(msg)
        } else {
            "".into()
        }
    }
}

fn regex_msg(msg: &str) -> Cow<str> {
    let re = Regex::new("sig: \"0x[a-fA-F0-9]+\"").unwrap();
    re.replace_all(msg, "sig: \"***filtered***\"")
}

这就是说“记录它会返回一个

Cow<str>
:它可能会从
&self
借用一个字符串,也可能不会”。

此外,我还修复了

regex_msg
的签名。首先,你应该总是更喜欢使用
&str
而不是
&String
作为参数(它更灵活,更惯用,而且你很少真正关心数据是否存储在
String
中或者例如文字中) 。其次,它现在像
Cow<str>
一样返回
Regex::replace_all
,避免在未找到匹配项时进行额外分配。最后,请不要命名变量
str
:它可能会让代码的读者感到困惑,因为它是内置类型。

游乐场链接


一个稍微简单一点的版本是这样做

pub trait MyLog<T>
where
    T: std::fmt::Debug
{
    fn debug_args(&self) -> T;
}

但是现在您无法返回

Cow<'_, str>
(因为
&self
T
的生命周期之间没有联系),因此您必须
impl MyLog<String>
并仅返回已分配的
String
。为了一些生命周期注释,我认为避免分配是完全值得的。

© www.soinside.com 2019 - 2024. All rights reserved.