【问题标题】:Cannot use format_args! due to temporary value is freed at the end of this statement不能使用 format_args!由于临时值在此语句的末尾被释放
【发布时间】:2019-05-25 11:18:43
【问题描述】:

我正在尝试构建自己的自定义 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!() 的调用中不使用任何 {} 占位符,问题就会消失。

实际解决方案

The solution from E_net4 is correct。它在我的实际代码中没有立即起作用,即:

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()
        );
    }
}

【问题讨论】:

    标签: rust


    【解决方案1】:

    format_args! 预计会在使用返回值的确切位置被调用。

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

    这是因为,就其实现方式而言,宏扩展为(在其他结构中)每个给定参数的范围狭窄的值序列,并且创建的 Arguments 值只有足够大的生命周期才能捕获他们。

    【讨论】:

    • 谢谢,这行得通。当我将它应用到我的实际代码而不是这个最小重现代码时它不起作用,但是我能够通过再次应用“内联调用”技术让它工作。我将编辑问题以显示实际的解决方案,因为其他人可能会觉得它很有用,尤其是如果他们是初学者。
    【解决方案2】:

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

    (|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"));
    

    【讨论】:

      猜你喜欢
      • 2019-06-01
      • 1970-01-01
      • 1970-01-01
      • 2019-11-06
      • 2012-12-16
      • 1970-01-01
      • 2019-04-18
      • 1970-01-01
      相关资源
      最近更新 更多