如何在Rust中实现类似C++宏的日志调用简化?
Rust实现自动填充文件与行号的日志调用
问题背景
我实现了一个用于错误日志的Trait:
trait LogTrait { fn log(self, mess: &str, file: &str, line: u32) -> Self; } impl<T, E> LogTrait for Result<T, E> where E: std::fmt::Display, { fn log(self, mess: &str, file: &str, line: u32) -> Self { if let Err(err) = &self { fs::write("errors.log", format!("{} :F{}L{}", mess, file, line)); } self } }
目前调用时需要手动传入file!()和line!():
SomeFunction.log("my message", file!(), line!())?
在C++中我会用宏实现仅传入消息,自动填充文件和行号:
#define LOG(MESSAGE) log(MESSAGE, file!(), line!())
想知道Rust中如何实现同样的需求?
解决方案
Rust的声明式宏可以轻松实现该需求,不需要依赖文本替换,以下是具体实现方式:
基础实现:封装调用的宏
定义一个声明式宏,自动为log方法填充file!()和line!()参数:
macro_rules! log_err { ($result:expr, $msg:expr) => { $result.log($msg, file!(), line!()) }; }
调用时只需传入结果对象和日志消息:
log_err!(SomeFunction, "my message")?
优化日志写入逻辑
原代码中fs::write会覆盖日志文件,建议改为追加写入,避免丢失历史错误记录:
use std::fs::OpenOptions; use std::io::Write; impl<T, E> LogTrait for Result<T, E> where E: std::fmt::Display, { fn log(self, mess: &str, file: &str, line: u32) -> Self { if let Err(err) = &self { let log_entry = format!("[{}] {} :F{}L{}\n", err, mess, file, line); let mut log_file = OpenOptions::new() .append(true) .create(true) .open("errors.log") .expect("无法打开错误日志文件"); writeln!(log_file, "{}", log_entry).expect("无法写入错误日志"); } self } }
可选:链式调用风格的宏
如果偏好链式调用的写法,可以定义如下宏(需在实现LogTrait的类型上下文内使用):
macro_rules! log { ($msg:expr) => { self.log($msg, file!(), line!()) }; }
调用示例:
fn some_operation() -> Result<(), Box<dyn std::error::Error>> { SomeFunction() .log!("my message")?; Ok(()) }
这种写法适用性有限,更推荐第一种log_err!宏的方案。
内容的提问来源于stack exchange,提问作者Agathe_DVY
相关产品推荐
相关产品推荐

