每种类型的未装箱封盖都是独一无二的 [英] Types of unboxed closures being unique to each

查看:41
本文介绍了每种类型的未装箱封盖都是独一无二的的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是在带有未装箱闭包的错误消息的问题的上下文中.答案指出,Rust 会生成一个类型,每个类型对于每个闭包都是唯一的,因为它们中的每一个都可能从封闭的作用域中捕获一组不同的变量.

This is in the context of the question at Error message with unboxed closures. The answers point out that Rust generates a type each that is unique to each closure, since each of them can potentially capture a different set of variables from the enclosing scope(s).

这是我的问题.该 FizzBu​​zz 示例中的两种不同的闭包类型标记不同,但看起来相同.编译器如何解决闭包类型差异,同时仍然查看类型参数的相同签名?

Here is my question. The two different closure types in that FizzBuzz example are labelled differently, but look identical. How does the compiler resolve the closure type differences while still looking at the identical signatures of the type parameters?

编译器所见与程序员所见之间的差距令人困惑.

This gap between what the compiler sees and what the programmer sees is confusing.

谢谢.

顺便说一下,R​​ust 参考文档第 8.1.10 节尚未对此进行任何说明.

By the way, Rust Reference document Section 8.1.10 does not yet say anything about this.

推荐答案

同样,我想从与该答案相同的示例开始.比较一下:

Again, I want to start with the same example as in that answer. Compare this:

fn show_both_1<S: Show>(x: S, y: S) {
    println!("{:?} {:?}", x, y);
}

还有这个:

fn show_both_2<S1: Show, S2: Show>(x: S1, y: S2) {
    println!("{:?} {:?}", x, y);
}

(由于最近的变化,现在使用 {:?} 而不是 {})

(now using {:?} instead of {} because of the recent changes)

第一个函数要求两个参数必须具有相同的类型,即使这种类型可以是任意的,只要它实现了Show:

The first function requires that both arguments must have the same type, even though this type can be arbitrary as long as it implements Show:

show_both_1::<i32>(1i32, 2i32);      // ok
show_both_1::<f64>(1.0f64, 2.0f64);  // ok
show_both_1::<???>(1i32, 2.0f64);    // not ok!

显然最后一个函数调用没有意义,因为参数的类型不同,但函数希望它们具有相同的类型.你甚至不能显式地编写类型参数——它应该是 i32 还是 f64?

Obviously the last function call does not make sense, because types of the arguments are different, but the function wants them to have the same type. You can't even write the type parameter explicitly - should it be i32 or f64?

第二个函数允许不同的类型,所以所有这些调用都可以:

The second function allows different types, so all of these calls are ok:

show_both_2::<i32, i32>(1, 2);
show_both_2::<f64, f64>(1.0, 2.0);
show_both_2::<i32, f64>(1, 2.0);

现在对于每个参数使用不同类型的参数,因此传递不同类型的值是完全没问题的,只要这两种类型都实现了Show.

Now for each argument a different type parameter is used, so it is perfectly fine to pass values of different types, as long as both of these types implement Show.

绝对同样的事情发生在闭包上.对于每个闭包,编译器生成一个新的唯一类型,它实现了 Fn* 特征之一.这些类型是匿名的,因此您无法命名它们:

Absolutely the same thing happens with closures. For each closure the compiler generates a new unique type which implements one of Fn* traits. These types are anonymous, so you can't name them:

let f: ??? = |&: x: i32, y: i32| x + y;

没有什么可以代替上面的 ??? 写的,但是没有必要,因为编译器知道它为闭包生成了哪种类型,因此它可以推断 f 的类型.真正确实重要的是,这种匿名类型将始终实现一种特殊特性:Fn, FnMut<代码>FnOnce.因此,如果您希望您的函数接受一个闭包,您需要向它传递一个实现这些特征之一的某种类型的实例.

There is nothing you can write instead of ??? above, but there is no need to because the compiler knows which type it has generated for the closure and so it can infer f's type. What really does matter is that this anonymous type will always implement one of special traits: Fn, FnMut or FnOnce. Consequently if you want your function to accept a closure, you need to pass it an instance of some type which implements one of these traits.

但这对于泛型来说是很自然的工作!当您希望您的函数接受实现某些已知特征的任意类型时,通常会使用它们,并且闭包的情况完全相同.所以你有这个:

But this is natural job for generics! They are usually used when you want your function to accept some arbitrary type which implements some known trait, and situation with closures is absolutely the same. So you have this:

fn call_closure<F: FnMut(i64) -> bool>(f: F) -> bool {
    f(10)
}

因为这个函数参数具有泛型类型,所以这个函数可以用于任何实现 FnMut(i64) -> 的类型.bool trait(这只是 FnMut<(i64,), bool> 的简写,包括编译器生成的闭包的匿名类型:

Because this function argument has generic type, this function can be used with any type which implements FnMut(i64) -> bool trait (which is just a shorthand for FnMut<(i64,), bool>, including anonymous types for closures generated by the compiler:

call_closure(|x| x > 10);
call_closure(|x| x == 42);

编译器将为这些闭包中的每一个生成一个唯一的类型,但是由于这些生成的类型将实现 FnMut(i64) ->bool trait,call_closure 会很乐意接受它们.

The compiler will generate a unique type for each of these closures, but since these generated types will implement FnMut(i64) -> bool trait, call_closure will happily accept both of them.

我在开头描述的不同类型参数的情况自然会扩展到闭包,因为这里使用了相同的机制,即特征.

The situation with different type parameters which I described in the beginning naturally extends to closures because the same mechanism is used here, that is, traits.

fn call_closures_2<F: FnMut(i64) -> bool>(f1: F, f2: F) -> bool {
    f1(10) && f2(20)
}

这个函数接受两个必须相同类型的参数,只要这个类型实现了FnMut(i64) ->bool 特性.这意味着此调用将不起作用:

This function accepts two arguments which must be of the same type as long as this type implements FnMut(i64) -> bool trait. And this means that this invocation won't work:

call_closures_2(|x| x > 9, |x| x == 20)

这是行不通的,因为这些闭包具有唯一,即不同的类型,但函数要求类型必须相同.例如,这确实有效:

It won't work because these closures have unique, i.e. different types, but the function requires that the types must be the same. For example, this does work:

fn call_closures_3<F: Fn(i64) -> bool>(f1: &F, f2: &F) -> bool {
    f1(10) && f2(20)
}

let f = |&: x: i64| x == 10;
call_closures_3(&f, &f);

请注意,函数参数必须仍然是相同的类型(为了示例方便,现在引用),但是由于我们调用它时引用了相同的闭包,因此它们的类型相同,一切正常.但这并不是很有用,因为它非常有限——通常你想为需要多个闭包的函数提供不同的闭包.

Note that the function arguments must still be of the same type (now references for the convenience of the example), but since we call it with references to the same closure, their type is the same, and everything works fine. This is not very useful though because it is very limiting - usually you want to provide different closures to functions which takes several ones.

为此,函数需要单独的类型参数以接受不同的闭包:

For this reason the function needs separate type parameters in order to accept different closures:

fn call_closures_4<F1, F2>(f1: F1, f2: F2) -> bool
        where F1: FnMut(i64) -> bool,
              F2: FnMut(i64) -> bool {
    f1(10) && f2(20)
}

call_closures_4(|x| x >= 9, |x| x <= 42)

现在类型参数是独立的,即使闭包有不同的匿名类型,用它们调用这个函数也是可以的:F1将成为第一个闭包的生成类型,F2 将成为第二个闭包的生成类型.

Now type parameters are independent, and even though closures have different anonymous types, it is ok to call this function with them: F1 will become the generated type of the first closure and F2 will become the generated type of the second closure.

这篇关于每种类型的未装箱封盖都是独一无二的的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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