【问题标题】:Closure Trait Bounds in Type Variables versus Closure Trait Bounds in Arguments on Higher Order Functions类型变量中的闭包特征边界与高阶函数参数中的闭包特征边界
【发布时间】:2016-07-17 23:50:47
【问题描述】:

为什么这两个有效:

fn apply_once1<F: FnOnce(T1) -> T2, T1, T2> (f: F, x: T1) -> T2 {
    f(x)
}

fn apply_once2<F, T1, T2> (f: F, x: T1) -> T2 
    where F: FnOnce(T1) -> T2 
{
    f(x)
}

但是这个不能编译:

fn apply_once3<T1, T2> (f: FnOnce(T1) -> T2, x: T1) -> T2 {
    f(x)
}

它抱怨:

error: the trait `core::marker::Sized` is not implemented for the type `core::ops::FnOnce(T1) -> T2 + 'static` [E0277]
    once3<T1, T2> (f: FnOnce(T1) -> T2, x: T1) -> T2 {
                   ^
help: see the detailed explanation for E0277
note: `core::ops::FnOnce(T1) -> T2 + 'static` does not have a constant size known at compile-time
note: all local variables must have a statically known size

我知道FnOnce 可能没有静态已知的大小,所以通常我会用&amp; 将变量交换为引用来解决这个问题,所以现在知道大小了。但我不明白为什么apply_once1apply_once2 能侥幸逃脱?

四处搜索,我找不到任何关于将特征绑定在参数上与将其放在类型变量上之间的区别的任何内容。

【问题讨论】:

    标签: closures rust higher-order-functions


    【解决方案1】:

    谷歌搜索,我找不到任何关于差异的内容 在将特征绑定到论点与将其绑定到论点之间 类型变量。

    这实际上不是你在第三个中所做的。让我们用一些更简单的东西来工作:

    fn do_something<C: Clone>(x: C); fn do_something<C>(x: C) where C: Clone;

    fn do_something(x: Clone)

    前两个实际上是同一件事,where 只是语法糖,使具有非平凡特征边界的函数更易于阅读,但两者都在说“我正在编写一个稍后将专门用于一种类型的函数实现克隆特征”。

    最后一句说

    “我想要 x,这实际上是克隆特征。”

    也许这很令人困惑,让我详细说明一下,任何带有尖括号的东西都可以被认为是函数的示意图,它是说对于满足某些要求的给定类型,编译器可以生成一个函数使用以下代码。这使我们无法编写:

    fn do_something(x: f64); fn do_something(x: Vec<usize>);

    等等。请注意缺少尖括号。如果没有尖括号,则说明您不是在编写原理图,而是在告诉编译器您想要的 exact 类型。现在,Clone 是一种类型……但它是一种特征。特征不像结构。实际上,您要求的是 Trait Object,因为它们没有大小,所以只能通过引用传递。

    您实际上并没有要求 实现 FnOnce 的对象,您实际上是在要求 FnOnce 的有效“柏拉图形式”,这不是您可以传递的东西,因为它是与其说是实际的东西,不如说是一个抽象的概念。您可以传入&amp;FnOnce,它表示“指向恰好是 FnOnce 的任何随机、任意事物的指针”,但这有一些权衡(我建议阅读有关 trait 对象的链接,或在它们上找到其他 SO 答案,其中涵盖的内容比此处更详细)。

    所以简短版本:函数签名中的任何内容都是具体类型,尖括号(和/或 where 子句)中的任何内容都是对这些具体类型之一的约束。

    【讨论】:

    • 谢谢它现在很有意义。就像FnOnce 是一个类型类约束(使用haskell 术语),类型类约束的使用与具体类型不同。比如f :: (Class a) =&gt; a -&gt; bf :: Class a -&gt; b。第二种形式不会输入检查,因为Class 有一种* -&gt; Constraint,而不是一种*。无论如何,apply_once3 因此是没有意义的。但是,正如您所说,还有其他 trait 对象可以像 &amp;FnMut 一样工作,而 &amp;FnOnce 由于借用而无法工作。
    • 是的,特征和类型类非常相似
    【解决方案2】:

    当您使用apply_once1 时,类型变量F 会使用已知大小的具体函数类型进行实例化。

    apply_once3 中,FnOnce(T1) -&gt; T2 是 trait 对象的类型,它没有已知的大小,因为可以使用不同的函数调用 apply_once3 的单个实例。

    this section of the Rust book

    【讨论】:

      猜你喜欢
      • 2020-08-17
      • 1970-01-01
      • 2016-11-29
      • 1970-01-01
      • 1970-01-01
      • 2023-03-20
      • 1970-01-01
      • 2016-12-23
      • 1970-01-01
      相关资源
      最近更新 更多