gpt4 book ai didi

rust - 未装箱的闭包类型各不相同

转载 作者:行者123 更新时间:2023-11-29 08:22:33 25 4
gpt4 key购买 nike

这是在 Error message with unboxed closures 问题的上下文中.答案指出 Rust 生成一个类型,每个闭包都是唯一的,因为它们中的每一个都可能从封闭范围捕获一组不同的变量。

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

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

谢谢。

编辑:顺便说一句,Rust 引用文档第 8.1.10 节还没有说明这一点。

最佳答案

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

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

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

第一个函数要求两个参数必须具有相同的类型,即使这种类型可以是任意的,只要它实现了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

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

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。 .

绝对相同的事情发生在闭包上。对于每个闭包,编译器生成一个新的唯一类型,它实现了 Fn* 之一。特质。这些类型是匿名的,所以你不能给它们命名:

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

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

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

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

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

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

编译器将为每个闭包生成一个唯一的类型,但由于这些生成的类型将实现 FnMut(i64) -> bool特质,call_closure会很乐意接受他们两个。

我一开始描述的不同类型参数的情况自然会延伸到闭包,因为这里使用了相同的机制,即特征。

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

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

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

它不会起作用,因为这些闭包具有唯一,即不同的类型,但函数要求类型必须相同。例如,这确实有效:

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

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

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

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将成为第二个闭包的生成类型。

关于rust - 未装箱的闭包类型各不相同,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27874683/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com