编译器允许我编写如下函数的全面实现:
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)
类型参数代表“输入”类型,而关联类型代表“输出”类型。
只要类型参数的组合是唯一的,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)
如您所见,该函数的结果类型是一个关联类型,名为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)
}
}
Run Code Online (Sandbox Code Playgroud)
这里,A使用 inFn的类型参数。
为什么这是无效的?理论上1,单个类型可以有多个Fn<Args>具有不同值的 的实现Args。在这种情况下,编译器应该选择哪种实现?您只能选择一个,因为A它没有作为类型参数传递给Invoke,因此F只能有一个Invoke.
1在实践中,您需要使用夜间编译器来执行此操作,因为实现Fn,FnMut或FnOnce直接 是一个不稳定的功能。在稳定版本上,编译器最多只会为函数和闭包生成这些特征中的每一个的一个实现。此外,即使在稳定的编译器上,任何其他具有类型参数的特征也可能遇到相同的问题。
也可以看看: