代码之家  ›  专栏  ›  技术社区  ›  Lukas Kalbertodt

为什么用“box”语法装箱一个函数指针数组只对临时的“let”绑定有效?

  •  6
  • Lukas Kalbertodt  · 技术社区  · 6 年前

    我有两个虚拟函数:

    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![] 找到了这个让我困惑的部分。

    2 回复  |  直到 6 年前
        1
  •  4
  •   Sven Marnach    6 年前

    在我看来,这是类型推理算法的一个特性,可能没有更深层次的原因,除非当前的推理算法恰好表现得像它一样。对于类型推断的工作时间和不工作时间,没有正式的规范。如果遇到类型推断引擎无法处理的情况,则需要添加类型批注,或以编译器能够正确推断类型的方式重写代码,这正是您在这里需要做的。

    铁锈的每一个功能都有其独特之处。 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 是为了在将来的某个时候为所有这些案例提供一个通用的解决方案,但我不知道该项目的状态和时间线是什么。

        2
  •  3
  •   Shepmaster Tim Diekmann    6 年前

    [T; N] [T] 是一个 unsizing coercion .

    CoerceUnsized<Pointer<U>> for Pointer<T> where T: Unsize<U> 是 为所有指针类型(包括智能指针,如 Box Rc )UNSIZE仅自动实现,并启用 以下转换:

    • [T; n] = & gt; [T]

    这些胁迫只发生在一定程度上 胁迫位点 :

    强制发生在强制站点。任何明确的位置 类型化将导致强制其类型。如果有必要推断, 不会执行强制。彻底地说,胁迫地点 对于表达式 e 键入 U 是:

    • let语句、静态和常量: let x: U = e
    • 函数参数: takes_a_U(e)
    • 将返回的任何表达式: fn foo() -> U { e }
    • 结构文本: Foo { some_u: e }
    • 数组文字: let x: [U; 10] = [e, ..]
    • Tuple literals: let x: (U, ..) = (e, ..)
    • 块中的最后一个表达式: let x: U = { ..; e }

    你的案子B是A let 语句,您的案例C是一个函数参数。您的案例A不包括在内。


    纯粹出于本能,我会指出 box 是一个不稳定的magic关键字,所以可能只实现了一半。也许它 应该 将强制应用于参数,但没有人需要它,因此它从未得到支持。