rust 无法使用format_args!由于临时值在此语句结束时被释放

a11xaf1n  于 2023-05-07  发布在  其他
关注(0)|答案(3)|浏览(122)

我正在尝试构造自己的自定义LogRecord并将其传递到log crate中。

use log::RecordBuilder;

fn main() {
    let msg = format_args!("Completed: {}, Elapsed={:?}", "blah", 20);
    //let msg = format_args!("This is OK");
    let mut builder = RecordBuilder::new();
    let _log_rec = builder
        .args(msg)
        .build();
}

我在调用args方法时遇到了一个临时的生存期问题。错误是

--> src/main.rs:4:28
  |
4 |     let msg = format_args!("Completed: {}, Elapsed={:?}", "blah", 20);
  |                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^             - temporary value is freed at the end of this statement
  |                            |
  |                            creates a temporary which is freed while still in use
...
8 |         .args(msg)
  |               --- borrow later used here
  |
  = note: consider using a `let` binding to create a longer lived value

通常情况下,这是一个简单的解决方案-只需将临时变量放入局部变量中。但是在这种情况下,我不知道如何修复它,因为我已经把我能想到的所有东西都放到了局部变量中(这就是为什么我不认为这个问题是其他问题的重复)。它似乎是format_args!宏特有的。
有趣的是,如果在对format_args!()的调用中不使用任何{}占位符,问题就会消失。

pw9qyyiw

pw9qyyiw1#

format_args!应该在使用返回值的地方被调用。

let mut builder = RecordBuilder::new();
let _log_rec = builder
    .args(format_args!("Completed: {}, Elapsed={:?}", "blah", 20))
    .build();

这是因为,作为它的实现方式,宏扩展为(在其他构造中)每个给定参数的窄范围值序列,并且Arguments值仅使用足够大的生命周期来捕获它们。

pu82cl6c

pu82cl6c2#

这里有一个使用闭包的解决方法。

(|msg: std::fmt::Arguments| {
    // use `msg` here...
})(format_args!("literal string"));

多亏了std::fmt::Arguments::as_str,使用解决方案的优点是您可以当场将std::fmt::Arguments转换为Cow<str>(例如:在宏内)以避免对文字字符串的分配。

(|msg: std::fmt::Arguments| {
    let msg: Cow<str> = match msg.as_str() {
        Some(literal) => literal.into(), // no format arguments, so it is a `&'static str`
        None => msg.to_string().into(),
    };

    // use `msg` here...
})(format_args!("literal string"));

如果你受到clippy的指责,只需将#[allow(clippy::redundant_closure_call)]放在闭包上。

#[allow(clippy::redundant_closure_call)]
(|msg: std::fmt::Arguments| {
    let msg: Cow<str> = match msg.as_str() {
        Some(literal) => literal.into(), // no format arguments, so it is a `&'static str`
        None => msg.to_string().into(),
    };

    // use `msg` here...
})(format_args!("literal string"));
wfypjpf4

wfypjpf43#

OP解决方案1:
The solution from E_net4正确。它在我的实际代码中没有立即工作,这是:

impl Drop for ExecutionTimer2 {
    fn drop(&mut self) {
        let elapsed = self.start_time.elapsed();

        let mut builder = RecordBuilder::new();
        let log_rec = builder
            .level(Level::Debug)
            .target("ExecutionTimer")
            .file(Some(self.file))
            .module_path(Some(self.module_path))
            .line(Some(self.line))
            .args(format_args!(
                "Completed: {}, Elapsed={:?}",
                self.name, elapsed
            ))
            .build();

        let logger = log::logger();
        logger.log(&log_rec);
    }
}

但是我再次应用了“内联”技术来得到这段代码,它可以编译:

impl Drop for ExecutionTimer2 {
    fn drop(&mut self) {
        let elapsed = self.start_time.elapsed();
        let mut builder = RecordBuilder::new();
        let logger = log::logger();

        logger.log(
            &builder
                .level(Level::Debug)
                .target("ExecutionTimer")
                .file(Some(self.file))
                .module_path(Some(self.module_path))
                .line(Some(self.line))
                .args(format_args!(
                    "Completed: {}, Elapsed={:?}",
                    self.name, elapsed
                ))
                .build(),
        );
    }
}

1由于它被编辑到问题中,因此必须将其删除,因为答案不属于问题。

相关问题