Rust中impl Fn()转fn()报错non-primitive cast,该如何解决?
Rust中
impl Fn()转fn()指针报错的原因与解决方法 问题代码与错误
尝试将impl Fn()类型转换为函数指针fn()时触发编译错误:
fn ptr_of(f: impl Fn()) -> fn() { f as fn() }
错误提示:
non-primitive cast: `impl Fn()` as `fn()`
上下文场景
实际要实现的功能代码如下(上述代码是该问题的最简复现):
trait FnExt { fn do_smth_with_ptr(&self); } impl<T: Fn()> FnExt for T { fn do_smth_with_ptr(&self) { let ptr: fn() = self as fn(); // 此处触发报错 todo!(); } } fn example() { example2.do_smth_with_ptr(); } fn example2() {};
原本预期代码能正常运行,因为直接将具体裸函数转换为fn()指针是可行的:
fn test() {} let ptr = test as fn();
原因分析
impl Fn()代表所有实现了Fn() trait的类型,其中不仅包含可直接转换为fn()指针的裸函数,还包括闭包、带环境的函数对象等类型——这些类型因携带额外环境数据,无法转换为无状态的fn()裸指针。
当你编写impl<T: Fn()> FnExt for T时,T的范围覆盖了所有满足Fn()的类型,包括不能转成fn()的闭包,因此Rust禁止这种强制转换,避免潜在的不安全场景。
而直接执行test as fn()能成功,是因为test是明确的裸函数类型,编译器可以确认转换的安全性。
解决方法
方法1:限制T为可转换为fn()的类型
修改trait实现的类型约束,要求T能转换为fn():
trait FnExt { fn do_smth_with_ptr(&self); } impl<T: Fn() + Into<fn()>> FnExt for T { fn do_smth_with_ptr(&self) { let ptr: fn() = (*self).into(); todo!(); } } fn example() { example2.do_smth_with_ptr(); } fn example2() {};
方法2:仅针对裸函数类型实现trait
如果只需要支持裸函数,直接限定实现的目标类型为fn():
trait FnExt { fn do_smth_with_ptr(&self); } impl FnExt for fn() { fn do_smth_with_ptr(&self) { let ptr: fn() = *self; todo!(); } } fn example() { example2.do_smth_with_ptr(); } fn example2() {};
方法3:使用不安全的强制转换(不推荐)
如果你能确保所有传入的T都可以安全转换为fn(),可以使用std::mem::transmute进行强制转换,但这种做法存在风险,若传入闭包会导致未定义行为:
trait FnExt { fn do_smth_with_ptr(&self); } impl<T: Fn()> FnExt for T { fn do_smth_with_ptr(&self) { unsafe { let ptr: fn() = std::mem::transmute(self); todo!(); } } } fn example() { example2.do_smth_with_ptr(); } fn example2() {};
内容的提问来源于stack exchange,提问作者bnjmn21
相关产品推荐
相关产品推荐

