类型变量中的闭包特征界限与高阶函数的参数中的闭包特征界限

时间:2016-03-30 06:09:56

标签: closures rust higher-order-functions

为什么这两个工作:

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可能没有静态已知的大小,所以通常情况下我会修改&交换变量作为参考,所以现在已知大小。但我不明白为什么apply_once1apply_once2可以逃脱它?

搜索周围,我找不到任何可以解释在参数上设置特征与将其放在类型变量上之间区别的内容。

2 个答案:

答案 0 :(得分:3)

  

在谷歌上搜索,我找不到任何可以解决差异的事情   把一个特征限制在论证上,而不是把它放在   类型变量。

这实际上并不是你在第三个方面所做的。让我们使用更简单的方法:

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

fn do_something(x: Clone)

前两个实际上是同一个东西,where只是语法糖,使得具有非平凡特征界限的函数更容易阅读,但两者都说&#34;我正在写一个函数,以后会专门用于实现克隆特征的类型&#34;。

最后一个是说

&#34;我想要x,字面意思是克隆特性。&#34;

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

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

等等。注意缺少尖括号。如果没有尖括号,你没有写一个原理图,你就告诉编译器你想要的完全类型。现在,Clone是一种类型......但它是一种特质。特征不像结构。事实上,你要求的是Trait Object,只能通过引用传递,因为它们的大小不合适。

你实际上并没有要求一个实施 FnOnce的对象,你实际上是在询问什么是有效的&#34;柏拉图形式&#34; FnOnce,这不是你可以传递的东西,因为它更像是一个抽象的概念,而不是一个真实的东西。你可以传入&FnOnce,这是指&#34;指向任何恰好是FnOnce&#34;的随机,随意的东西的指针,但是有一些权衡(我建议阅读关于特征对象的链接,或者在他们身上找到其他SO答案,这些答案将更详细地介绍它们。)

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

答案 1 :(得分:2)

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

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

请参阅this section of the Rust book