Rust:如何判断类型是否为Result/实现Try trait?宏方案求助
Rust活动日志器实现:类型判断与宏封装问题
我刚接触Rust,需要了解该语言的类型检查机制,目标是实现一个活动日志器,用来捕获并记录函数的执行时长与结果。以下是我的简化实现:
struct Activity { name: String, start_time: std::time::Instant, } impl Activity { fn begin(name: &str) -> Self { println!("Activity started: {}", name); Activity { name: name.to_string(), start_time: std::time::Instant::now(), } } fn end_ok(&self) { let duration = self.start_time.elapsed().as_millis(); println!("Activity ended: {}, Status: Success, Duration: {}", self.name, duration); } fn end_error<T: ToString>(&self, error: &T) { let duration = self.start_time.elapsed().as_millis(); println!( "Activity ended: {}, Status: Fail, Duration: {}, Message: {}", self.name, duration, error.to_string() ); } } type MyResult = Result<(), String>; fn test1() -> Result<(), Box<dyn std::error::Error>> { test2()?; Ok(()) } fn test2() -> MyResult { Err("oops".to_string()) } fn main() { let activity = Activity::begin("TestActivity"); match test1() { Ok(_) => activity.end_ok(), Err(error) => activity.end_error(&error), } }
当前实现虽然能用,但手动分析每个结果并构建结果链的方式太繁琐,我更想使用?运算符,但这需要对结果处理进行封装。我尝试用proc_macro_attribute来做封装,但遇到了判断函数返回类型的难题。
我的目标是识别函数块的返回值类型:如果是Result<_, _>且返回Err,就调用end_error(error);否则调用end_ok()。以下是我尝试的宏实现:
extern crate proc_macro; use proc_macro::TokenStream; use quote::quote; use syn::{parse_macro_input, ItemFn, LitStr, ReturnType}; #[proc_macro_attribute] pub fn activity_logger(attr: TokenStream, item: TokenStream) -> TokenStream { let logger_name = parse_macro_input!(attr as LitStr).value(); let input_fn = parse_macro_input!(item as ItemFn); let fn_name = &input_fn.sig.ident; let fn_block = &input_fn.block; let fn_return_type = &input_fn.sig.output; let output = match fn_return_type { ReturnType::Type(_, _) => quote! { fn #fn_name() #fn_return_type { let activity = Activity::begin(#logger_name); let result = (|| #fn_block)(); // C#风格伪代码开始 if result is Result<_, _> { match result { Ok(_) => activity.end_ok(), Err(error) => activity.end_error(error), } } else { activity.end_ok(); } // C#风格伪代码结束 result } }, ReturnType::Default => quote! { fn #fn_name() { let activity = Activity::begin(#logger_name); #fn_block activity.end_ok(); } }, }; output.into() }
有没有办法在运行时判断一个对象是否实现了Try trait或者是否为Result<_, _>?或者用宏能否解决这个问题?
我目前找到的唯一识别返回类型的方式是通过ReturnType::Type(_, ty)提取类型名,或者用std::any::type_name,但这并不理想,因为结果类型可能被重命名。
我还尝试用trait来解决,但遇到了问题:
- Rust不允许这类代码,因为存在实现歧义:
trait IsResult { const IS_RESULT: bool; } impl<T, E> IsResult for Result<T, E> { const IS_RESULT: bool = true; } impl<T> IsResult for T { const IS_RESULT: bool = false; }
- 而且Rust的trait和其他语言的接口不同,以下方法也无效:
trait Loggable { fn can_log(&self) -> bool; } trait LoggableResult: Loggable { fn can_log(&self) -> bool; } impl<T> Loggable for T { fn can_log(&self) -> bool { false } } impl<T, E> LoggableResult for Result<T, E> { fn can_log(&self) -> bool { true } } fn can_log(value: &dyn Loggable) -> bool { value.can_log() } fn test() { let test1_result = 123; let test1 = can_log(&test1_result); // false let test2_result: Result<(), String> = Result::Err("oops".to_string()); let test2 = can_log(&test2_result); // false }
内容的提问来源于stack exchange,提问作者evgenxpp
相关产品推荐
相关产品推荐

