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

swi*_*ard 4 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()
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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)
    }
}
Run Code Online (Sandbox Code Playgroud)
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()
    }
}
Run Code Online (Sandbox Code Playgroud)

我不明白为什么这两种情况不同。不是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)
    }
}
Run Code Online (Sandbox Code Playgroud)

Fra*_*gné 5

类型参数代表“输入”类型,而关联类型代表“输出”类型。

只要类型参数的组合是唯一的,Rust 就允许您实现一个泛型特征的多个实例。例如,一个人struct Foo可以实现PartialEq<Foo>PartialEq<Bar>一起。

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

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


trait(及其友元和)的Fn(Args) -> Output语法隐藏了这些 trait 的底层实现。这是您再次使用不稳定的“低级”语法:FnFnMutFnOnceimpl

#![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()
    }
}
Run Code Online (Sandbox Code Playgroud)

如您所见,该函数的结果类型是一个关联类型,名为OutputOutput = 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)
    }
}
Run Code Online (Sandbox Code Playgroud)

这里,A使用 inFn的类型参数。

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

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

也可以看看: