无法确定带函数Trait的返回类型,遇E0308类型不匹配错误
Rust中tracing-subscriber返回类型不匹配(E0308)
问题说明
尝试从函数返回tracing-subscriber相关的复杂类型时,持续触发E0308类型不匹配错误:预期的Handle类型使用了函数指针形式的FilterFn,但实际返回的是包含闭包的FilterFn类型。移除函数返回类型后程序可正常运行,print_type_of能打印类型,但直接使用该打印类型仍无法解决问题。同时希望将该类型存储在自定义结构体struct MyStruct { x: FmtHandle }中。
错误详情
error[E0308]: mismatched types --> src/main.rs:45:5 | 20 | fn my_function() -> FmtHandle { | --------- expected `Handle<tracing_subscriber::fmt::Layer<Layered<FilterFn<for<'a, 'b> fn(&'a tracing::Metadata<'b>) -> bool>, Registry>, DefaultFields, Format, BoxMakeWriter>, Layered<FilterFn<for<'a, 'b> fn(&'a tracing::Metadata<'b>) -> bool>, Registry>>` because of return type ... 27 | let env = tracing_subscriber::filter::FilterFn::new(|metadata| { | ---------- the found closure ... 45 | fmt_handle | ^^^^^^^^^^ expected `Handle<Layer<Layered<FilterFn<fn(&...) -> ...>, ...>, ..., ..., ...>, ...>`, found `Handle<Layer<Layered<FilterFn<...>, ...>, ..., ..., ...>, ...>` | = note: expected struct `Handle<tracing_subscriber::fmt::Layer<Layered<FilterFn<for<'a, 'b> fn(&'a tracing::Metadata<'b>) -> bool>, _>, _, _, _>, Layered<FilterFn<for<'a, 'b> fn(&'a tracing::Metadata<'b>) -> bool>, _>>` found struct `Handle<tracing_subscriber::fmt::Layer<Layered<FilterFn<[closure@src/main.rs:27:57: 27:67]>, _>, _, _, _>, Layered<FilterFn<[closure@src/main.rs:27:57: 27:67]>, _>>` For more information about this error, try `rustc --explain E0308`. error: could not compile `test-bin` (bin "test-bin") due to previous error
原始代码
use tracing_subscriber::{fmt, prelude::*, reload}; type FilterClosure = fn(_: &tracing::Metadata<'_>) -> bool; type FmtHandle = tracing_subscriber::reload::Handle< tracing_subscriber::fmt::Layer< tracing_subscriber::layer::Layered< tracing_subscriber::filter::FilterFn<FilterClosure>, tracing_subscriber::registry::Registry, >, tracing_subscriber::fmt::format::DefaultFields, tracing_subscriber::fmt::format::Format, tracing_subscriber::fmt::writer::BoxMakeWriter, >, tracing_subscriber::layer::Layered< tracing_subscriber::filter::FilterFn<FilterClosure>, tracing_subscriber::registry::Registry, >, >; fn my_function() -> FmtHandle { let fmt_layer = fmt::Layer::new().with_writer( tracing_subscriber::fmt::writer::BoxMakeWriter::new(std::io::stdout), ); let (fmt_layer, fmt_handle) = reload::Layer::new(fmt_layer); print_type_of(&fmt_handle); let env = tracing_subscriber::filter::FilterFn::new(|metadata| { println!("file: {:?}", metadata.file()); println!("module: {:?}", metadata.module_path()); *metadata.level() <= tracing_subscriber::filter::LevelFilter::ERROR }); tracing_subscriber::registry::Registry::default() .with(env) .with(fmt_layer) .try_init() .unwrap(); tracing::trace!("test trace"); tracing::debug!("test debug"); tracing::info!("test info"); tracing::warn!("test warn"); tracing::error!("test error"); fmt_handle } fn main() { let _ = my_function(); } fn print_type_of<T>(_: &T) { println!("{}", std::any::type_name::<T>()) }
依赖配置
tracing = { version = "0.1.37" } tracing-subscriber = { version = "0.3.17" }
解决方案
核心原因
闭包是匿名唯一类型,即使逻辑和函数指针完全一致,它的类型也不等于fn(...) -> ...函数指针类型。你定义的FilterClosure是函数指针,但实际代码中使用的是闭包,导致FilterFn的泛型参数不匹配,最终Handle的类型完全不兼容。
方案1:将闭包强制转换为函数指针(仅适用于无捕获的闭包)
你的闭包没有捕获任何外部变量,符合转换为函数指针的条件。直接将闭包强转为fn(&tracing::Metadata<'_>) -> bool即可:
// 修改闭包定义部分 let env = tracing_subscriber::filter::FilterFn::new( (|metadata| { println!("file: {:?}", metadata.file()); println!("module: {:?}", metadata.module_path()); *metadata.level() <= tracing_subscriber::filter::LevelFilter::ERROR }) as fn(&tracing::Metadata<'_>) -> bool );
此时FilterFn的类型会匹配你定义的FilterClosure,FmtHandle类型可以正常使用,包括存入自定义结构体。
方案2:使用动态分发(Trait Object)适配任意Filter类型
如果后续闭包需要捕获环境,无法转为函数指针,可以用Box<dyn Filter + Send + Sync + 'static>做动态分发:
- 修改
FmtHandle类型定义:
type FmtHandle = tracing_subscriber::reload::Handle< tracing_subscriber::fmt::Layer< tracing_subscriber::layer::Layered< Box<dyn tracing_subscriber::filter::Filter + Send + Sync + 'static>, tracing_subscriber::registry::Registry, >, tracing_subscriber::fmt::format::DefaultFields, tracing_subscriber::fmt::format::Format, tracing_subscriber::fmt::writer::BoxMakeWriter, >, tracing_subscriber::layer::Layered< Box<dyn tracing_subscriber::filter::Filter + Send + Sync + 'static>, tracing_subscriber::registry::Registry, >, >;
- 调整闭包的包装方式:
let env = Box::new(tracing_subscriber::filter::FilterFn::new(|metadata| { println!("file: {:?}", metadata.file()); println!("module: {:?}", metadata.module_path()); *metadata.level() <= tracing_subscriber::filter::LevelFilter::ERROR })) as Box<dyn tracing_subscriber::filter::Filter + Send + Sync + 'static>;
这种方式可以兼容任意实现Filter trait的类型,包括带捕获的闭包、函数指针等。
内容的提问来源于stack exchange,提问作者Jonathan Woollett-light
相关产品推荐
相关产品推荐

