Reputation: 21
I want to filter the msg
in debug_args
function before returning:
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 {
&""
}
}
}
I want to use Regex on msg but I get this error:
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() {
®ex_msg(msg)
} else {
&""
}
}
I understand that this is trying to return a reference to a value that is local to the function and once the function returns, its local values are dropped and any references to them would dangle.
I also tried cloning the msg
string in a new function & returning a reference to it which is also not allowed by the compiler.
Finally, I think a workaround exists with Cow<str>
but I don't wanna use it.
Q. How can I achieve the filtering operation on the above msg
without changing the function signature if possible?
Upvotes: 2
Views: 954
Reputation: 4360
A returned &str
always has a lifetime. So if you want to return a &str
that references data you created in the function, you have, in general, two possibilities:
self
object, and return a reference with the lifetime of &self
.&'static str
(in general, don't do this! It may be ok in setup code where the lifetime of the data really should be the lifetime of the program, but that don't seem to be the case in the question).Maybe you could apply the regex when storing message (assuming self.message
is a private field)?
Or, as a different variation of the theme, you could store message
not as an Option<String>
but as an Option<MySecretString>
, return a reference to that directly and put the regex handling in the Debug
impl of MySecretString
. I think this is what I would do.
Upvotes: 0
Reputation: 2067
Personally, this is how I'd approach this:
If you really cannot change the signature of debug_args
, then I'd store the filtered message in a String
member of your struct so you can return a reference to it. However, MyLog
looks like your own trait, so I'd really advise you change that instead.
I would change MyLog
to look like this:
pub trait MyLog<'a, T>
where
T: std::fmt::Debug + 'a
{
fn debug_args(&'a self) -> T;
}
This is much more idiomatic than returning a &dyn Trait
(which has its place, but isn't necessary here from what I can see). This is basically saying "you can implement this to return any type, so long as that type implements Debug
and lives at least as long as the &self
you borrow".
Then you can implement it like this:
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***\"")
}
This is saying "logging this returns a Cow<str>
: it might borrow a string from &self
or it might not".
Additionally, I've fixed up the signature of regex_msg
. First, you should always prefer &str
over &String
as a parameter (it's more flexible, more idiomatic, and very very rarely do you actually care whether the data is stored in a String
or, for example, a literal). Second, it now returns Cow<str>
just like Regex::replace_all
, avoiding an extra allocation if no matches were found. Finally, please don't name a variable str
: it will probably confuse readers of your code as it's a built-in type.
A very slightly simpler version would be to instead do
pub trait MyLog<T>
where
T: std::fmt::Debug
{
fn debug_args(&self) -> T;
}
But now you couldn't return a Cow<'_, str>
(since there's no link between the lifetime of &self
and T
), so you'd have to impl MyLog<String>
and just return an allocated String
. For the sake of a couple of lifetime annotations I think that avoiding the allocation is totally worth it.
Upvotes: 1