我正在尝试构建自己的自定义LogRecord并将其传递到log板条箱中。
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!()
占位符,问题就会消失。
format_args!
预计将在使用返回值的地方准确调用。
let mut builder = RecordBuilder::new();
let _log_rec = builder
.args(format_args!("Completed: {}, Elapsed={:?}", "blah", 20))
.build();
这是因为,就其实现方式而言,宏扩展为(以及其他构造)每个给定参数的一系列范围狭窄的值,并且
Arguments
值的创建仅具有足够大的生命周期来捕获他们。
这是使用闭包的解决方法。
(|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"));
OP的解决方案1:
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 由于它被编辑到问题中,因此必须将其删除,因为答案不属于问题。
由于 Rust 还没有解决这些问题,我扩展了一个更好的解决方案(没有闭包,所以不需要关闭 Clippy)并发布了它。