为封闭特征(Fn)创建封面实现时,为什么会得到“类型参数不受约束”?

时间:2016-05-22 17:36:51

标签: rust

编译器允许我编写一个毯子实现这样的函数:

trait Invoke {
    type S;
    type E;

    fn fun(&mut self) -> Result<Self::S, Self::E>;
}

impl<F, S, E> Invoke for F
where
    F: Fn() -> Result<S, E>,
{
    type S = S;
    type E = E;

    fn fun(&mut self) -> Result<S, E> {
        self()
    }
}

但是当我尝试添加一个函数参数时,它开始抱怨:

trait Invoke {
    type A;
    type S;
    type E;

    fn fun(&mut self, arg: Self::A) -> Result<Self::S, Self::E>;
}

impl<F, A, S, E> Invoke for F
where
    F: Fn(A) -> Result<S, E>,
{
    type A = A;
    type S = S;
    type E = E;

    fn fun(&mut self, arg: A) -> Result<S, E> {
        self(arg)
    }
}
error[E0207]: the type parameter `A` is not constrained by the impl trait, self type, or predicates
 --> src/lib.rs:9:9
  |
9 | impl<F, A, S, E> Invoke for F
  |         ^ unconstrained type parameter

error[E0207]: the type parameter `S` is not constrained by the impl trait, self type, or predicates
 --> src/lib.rs:9:12
  |
9 | impl<F, A, S, E> Invoke for F
  |            ^ unconstrained type parameter

error[E0207]: the type parameter `E` is not constrained by the impl trait, self type, or predicates
 --> src/lib.rs:9:15
  |
9 | impl<F, A, S, E> Invoke for F
  |               ^ unconstrained type parameter

我无法理解为什么这两种情况不同。 A不是约束签名的一部分吗?

我意识到我可以像Fn特征声明一样重写它,但我仍然不明白:

trait Invoke<A> {
    type S;
    type E;

    fn fun(&mut self, arg: A) -> Result<Self::S, Self::E>;
}

impl<F, A, S, E> Invoke<A> for F
where
    F: Fn(A) -> Result<S, E>,
{
    type S = S;
    type E = E;

    fn fun(&mut self, arg: A) -> Result<S, E> {
        self(arg)
    }
}

1 个答案:

答案 0 :(得分:2)

类型参数代表&#34;输入&#34;类型,而相关类型代表&#34;输出&#34;类型。

Rust允许您实现通用特征的多个实例,只要类型参数的组合是唯一的。例如,单个struct Foo可以同时实现PartialEq<Foo>PartialEq<Bar>

相反,关联类型由特征实现分配。例如,Add特征具有类型参数RHS和关联类型Output。对于Self(实施特征的类型)和RHS的每个组合,关联的类型Output都是固定的。

使用关联类型的主要原因是减少特征上类型参数的数量,特别是在使用该特征可能必须定义类型参数以正确绑定该特征的情况下。但是,相关类型并不总是合适的;这就是为什么我们仍然有类型参数!

Fn(Args) -> Output特征的Fn语法(及其朋友FnMutFnOnce)隐藏了这些特征的基础实现。这是您第一次impl再次使用不稳定的&#34;低级别&#34;语法:

#![feature(unboxed_closures)]

impl<F, S, E> Invoke for F
where
    F: Fn<(), Output = Result<S, E>>,
{
    type S = S;
    type E = E;

    fn fun(&mut self) -> Result<S, E> {
        self()
    }
}

如您所见,函数的结果类型是一个名为Output的关联类型。 Output = Result<S, E>是谓词,因此满足编译器对impl块上类型参数的条件之一。

现在,您的第二个impl语法不稳定了:

#![feature(unboxed_closures)]

impl<F, A, S, E> Invoke for F
where
    F: Fn<(A,), Output = Result<S, E>>,
{
    type A = A;
    type S = S;
    type E = E;

    fn fun(&mut self, arg: A) -> Result<S, E> {
        self(arg)
    }
}

此处A的{​​{1}}类型参数中使用了Fn

为什么这不起作用?在理论上 1 ,单个类型可以具有Fn<Args>的多个实现,具有不同的Args值。在这种情况下,编译器应该选择哪种实现?您只能选择一个,因为A未作为Invoke的类型参数传递,因此F只能有一个Invoke的实现。

1 实际上,您需要使用夜间编译器来执行此操作,因为直接实施FnFnMutFnOnce是一项不稳定的功能。在稳定版本中,编译器将仅为函数和闭包生成每个特征的最多一个实现。此外,即使在稳定的编译器上,您也可能遇到与具有类型参数的任何其他特征相同的问题。

另见: