我有两个虚拟功能:
fn foo() -> u32 { 3 }
fn bar() -> u32 { 7 }
我想创建一个盒装的函数指针切片:Box<[fn() -> u32]>
。我想使用box
语法(我知道两个元素不是必需的,但是我的实际用例是不同的。)
我尝试了几件事(Playground):
// Version A
let b = box [foo, bar] as Box<[_]>;
// Version B
let tmp = [foo, bar];
let b = box tmp as Box<[_]>;
// Version C
let b = Box::new([foo, bar]) as Box<[_]>;
版本B和C可以正常工作(尽管C对我来说不起作用,因为它使用Box::new
),但是版本A错误:
error[E0605]: non-primitive cast: `std::boxed::Box<[fn() -> u32; 2]>` as `std::boxed::Box<[fn() -> u32 {foo}]>`
--> src/main.rs:8:13
|
8 | let b = box [foo, bar] as Box<[_]>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait
显然,由于某种原因,在版本A中,编译器无法将功能项强制转换为功能指针。 那是为什么?为何还要与附加的临时let
绑定一起使用?
此问题的灵感来自this other question。我想知道为什么vec![foo, bar]
出错,但是[foo, bar]
正常工作。我看着the definition of vec![]
,发现这部分令我感到困惑。
答案 0 :(得分:4)
在我看来,这似乎是类型推断算法的特质,除了当前的推断算法的行为恰似它之外,可能没有更深层次的原因。对于类型推断何时起作用以及何时不起作用没有正式的规范。如果遇到类型推断引擎无法处理的情况,则需要添加类型注释或以编译器可以正确推断类型的方式重写代码,而这正是您在此处要做的。
Rust中的每个函数都有其自己的单独的function item type,无法通过语法直接命名,但是会被显示为例如错误消息中的fn() -> u32 {foo}
。如果它们出现在match
的不同分支,if
的不同分支或数组的不同元素中,则存在一种特殊的强制转换,将具有相同签名的函数项类型转换为相应的函数指针类型。 。这种强制与其他强制不同,因为它不仅发生在显式类型的上下文(“强制站点”)中,而且这种特殊的处理方式很可能导致这种特殊性。
特殊强制是由绑定触发的
let tmp = [foo, bar];
因此tmp
的类型完全确定为[fn() -> u32; 2]
。但是,似乎在编写时在类型推断算法中还没有足够早地触发特殊强制
let b = box [foo, bar] as Box<[_]>;
编译器首先假定数组的项目类型为其第一个元素的类型,显然当试图确定_
在此处表示什么时,编译器仍未更新此概念–根据错误消息_
在这里被推断为fn() -> u32 {foo}
。有趣的是,编译器在打印错误消息时已经正确推断出box [foo, bar]
的完整类型,因此该行为的确很奇怪。仅当详细查看编译器源代码时才能给出完整的解释。
Rust的类型求解器引擎通常无法处理其理论上应该能够解决的情况。 Niko Matsakis的chalk engine旨在在将来某个时候为所有这些情况提供一个通用的解决方案,但我不知道该项目的状态和时间表是什么。
答案 1 :(得分:3)
[T; N]
至[T]
是unsizing coercion。
CoerceUnsized<Pointer<U>> for Pointer<T> where T: Unsize<U>
是 为所有指针类型实现(包括智能指针,例如Box
和Rc
)。 Unsize仅自动实现,并启用 以下转换:
[T; n]
=>[T]
这些强制仅发生在某些强制位置:
强制发生在强制位置。任何明确的位置 输入将导致强制转换为其类型。如果需要推断, 强制将不会执行。穷尽性的强制场所 对于表达式
e
键入U
的情况是:
- 让语句,静态变量和常量:
let x: U = e
- 函数参数:
takes_a_U(e)
- 将返回的任何表达式:
fn foo() -> U { e }
- 结构文字:
Foo { some_u: e }
- 数组文字:
let x: [U; 10] = [e, ..]
- 元组文字:
let x: (U, ..) = (e, ..)
- 块中的最后一个表达式:
let x: U = { ..; e }
您的情况B是一个let
语句,您的情况C是一个函数参数。您的案件A没有承保。
纯粹出于直觉,我要指出box
是一个不稳定的魔术关键字,因此它可能只是实现了一半。也许应该施加强制性,但是没有人需要它,因此它从未得到支持。