为什么我得到“类型参数不受约束"?在为闭包特征 (Fn) 创建全面实现时? [英] Why do I get "the type parameter is not constrained" when creating a blanket implementation for a closure trait (Fn)?

查看:41
本文介绍了为什么我得到“类型参数不受约束"?在为闭包特征 (Fn) 创建全面实现时?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

编译器允许我编写如下函数的全面实现:

The compiler allows me to write blanket implementation a function like this:

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()
    }
}

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

but it starts complaining when I try to add a function parameter:

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 不是约束签名的一部分吗?

I cannot understand why these two cases are different. Isn't A a part of constraint signature?

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

I realized I can rewrite it like the Fn trait declaration, but I still do not get the idea:

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)
    }
}

推荐答案

类型参数代表输入"类型,而关联类型代表输出"类型.

Type parameters represent "input" types, while associated types represent "output" types.

Rust 允许你实现一个泛型特征的多个实例,只要类型参数的组合是唯一的.例如,单个 struct Foo 可以一起实现 PartialEqPartialEq.

Rust allows you to implement multiple instances of a generic trait so long as the combination of type parameters are unique. For example, a single struct Foo could implement PartialEq<Foo> and PartialEq<Bar> together.

相反,关联类型由 trait 实现分配.例如,Add trait 有一个类型参数 RHS 和一个关联的类型 Output.对于 Self(实现 trait 的类型)和 RHS 的每个组合,关联类型 Output 是固定的.

In contrast, associated types are assigned by the trait implementation. For example, the Add trait has a type parameter, RHS, and an associated type, Output. For each combination of Self (the type on which the trait is implemented) and RHS, the associated type Output is fixed.

使用关联类型的主要原因是减少 trait 的类型参数数量,尤其是在使用该 trait 可能必须定义类型参数以正确绑定该 trait 的情况下.然而,关联类型并不总是合适的;这就是为什么我们还有类型参数!

The main reason for using associated types is to reduce the number of type parameters on traits, especially where uses of that trait might have to define a type parameter just to properly bound that trait. However, associated types are not always appropriate; that's why we still have type parameters!

Fn(Args) ->Fn trait(以及它的朋友 FnMutFnOnce)的输出 语法隐藏了这些 trait 的底层实现.这是您的第一个 impl 再次使用不稳定的低级"语法:

The Fn(Args) -> Output syntax for the Fn trait (and its friends FnMut and FnOnce) hides the underlying implementation of these traits. Here's your first impl again with the unstable "low-level" syntax:

#![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 是一个谓词,因此它满足编译器对 impl 块上的类型参数的条件之一.

As you can see, the function's result type is an associated type, named Output. Output = Result<S, E> is a predicate, so that satisfies one of the compiler's conditions for type parameters on impl blocks.

现在,这是你的第二个 impl 语法不稳定:

Now, here's your second impl with the unstable syntax:

#![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用于Fn的类型参数.

Here, A is used in Fn's type parameter.

为什么这无效?理论上1,单个类型可以有多个Fn 的实现,其中Args 的值不同.在这种情况下,编译器应该选择哪种实现?你只能选择一个,因为A没有作为类型参数传递给Invoke,因此F只能有一个<代码>调用.

Why is this not valid? In theory1, a single type could have multiple implementations of Fn<Args> with different values of Args. Which implementation should the compiler select in that case? You can only choose one, because A is not passed as a type parameter to Invoke, and thus F can only have a single implementation of Invoke.

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

1 In practice, you need to use a nightly compiler to do this, because implementing Fn, FnMut or FnOnce directly is an unstable feature. On a stable versions, the compiler will only generate up to one implementation of each of these traits for functions and closures. Also, you could have the same issue with any other trait that has type parameters, even on a stable compiler.

另见:

这篇关于为什么我得到“类型参数不受约束"?在为闭包特征 (Fn) 创建全面实现时?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆