gpt4 book ai didi

rust - 为什么 `std::mem::drop` 与高级特征边界中的闭包 |_|() 不完全相同?

转载 作者:行者123 更新时间:2023-12-04 01:06:44 24 4
gpt4 key购买 nike

std::mem::drop 的执行记录如下:

pub fn drop<T>(_x: T) { }

因此,我希望关闭 |_| () (俗称 toilet closure )是 drop 的潜在 1:1 替代品, 在两个方向。但是,下面的代码显示 drop与函数参数上的更高等级特征不兼容,而厕所关闭是。

fn foo<F, T>(f: F, x: T)
where
for<'a> F: FnOnce(&'a T),
{
dbg!(f(&x));
}

fn main() {
foo(|_| (), "toilet closure"); // this compiles
foo(drop, "drop"); // this does not!
}

编译器的错误信息:

error[E0631]: type mismatch in function arguments
--> src/main.rs:10:5
|
1 | fn foo<F, T>(f: F, x: T)
| ---
2 | where
3 | for<'a> F: FnOnce(&'a T),
| ------------- required by this bound in `foo`
...
10 | foo(drop, "drop"); // this does not!
| ^^^
| |
| expected signature of `for<'a> fn(&'a _) -> _`
| found signature of `fn(_) -> _`

error[E0271]: type mismatch resolving `for<'a> <fn(_) {std::mem::drop::<_>} as std::ops::FnOnce<(&'a _,)>>::Output == ()`
--> src/main.rs:10:5
|
1 | fn foo<F, T>(f: F, x: T)
| ---
2 | where
3 | for<'a> F: FnOnce(&'a T),
| ------------- required by this bound in `foo`
...
10 | foo(drop, "drop"); // this does not!
| ^^^ expected bound lifetime parameter 'a, found concrete lifetime

考虑到 drop据说对于任何大小的 T 都是通用的,“更通用”的签名 fn(_) -> _ 听起来不合理与 for<'a> fn (&'a _) -> _ 不兼容.为什么编译器不承认 drop 的签名在这里,当马桶关闭代替它时,有什么不同?

最佳答案

问题的核心是drop不是单个函数,而是一组参数化的函数,每个函数都删除某些特定类型。为了满足更高级别的特征绑定(bind)(以下称为 hrtb),您需要一个可以同时引用具有任何给定生命周期的类型的函数。

我们将使用 drop作为我们泛型函数的典型示例,但所有这些也更普遍适用。以下是引用代码:fn drop<T>(_: T) {} .

从概念上讲,drop不是单个函数,而是每个可能类型的一个函数T . drop 的任何特定实例只接受单一类型的参数。这称为 monomorphization .如果不同 Tdrop 一起使用, drop 的不同版本被编译。这就是为什么您不能将泛型函数作为参数传递并完全通用地使用该函数(参见 this question )

另一方面,像 fn pass(x: &i32) -> &i32 {x} 这样的函数满足 hrtb for<'a> Fn(&'a i32) -> &'a i32 .不像 drop ,我们有一个函数同时满足 Fn(&'a i32) -> &'a i32每一生'a .这反射(reflect)在 pass可以使用。

fn pass(x: &i32) -> &i32 {
x
}

fn two_uses<F>(f: F)
where
for<'a> F: Fn(&'a i32) -> &'a i32, // By the way, this can simply be written
// F: Fn(&i32) -> &i32 due to lifetime elision rules.
// That applies to your original example too.
{
{
// x has some lifetime 'a
let x = &22;
println!("{}", f(x));
// 'a ends around here
}
{
// y has some lifetime 'b
let y = &23;
println!("{}", f(y));
// 'b ends around here
}
// 'a and 'b are unrelated since they have no overlap
}

fn main() {
two_uses(pass);
}


(playground)

在示例中,生命周期 'a'b彼此没有关系:两者都不完全包含对方。所以这里没有发生某种子类型化的事情。 pass 的单个实例确实被用于两个不同的、不相关的生命周期。

这就是为什么 drop不满足 for<'a> FnOnce(&'a T) . drop 的任何特定实例只能涵盖一个生命周期(忽略子类型)。如果我们通过 drop进入 two_uses从上面的例子中(有轻微的签名变化并假​​设编译器允许我们),它必须选择一些特定的生命周期 'adrop 的实例在 two_uses 范围内将是 Fn(&'a i32)一些具体的生命周期 'a .由于该函数仅适用于单个生命周期 'a ,不可能在两个不相关的生命周期中使用它。

那么为什么马桶关闭会获得 hrtb 呢?在推断闭包的类型时,如果预期类型暗示需要更高级别的 trait bound, the compiler will try to make one fit .在这种情况下,它成功了。

Issue #41078与此密切相关,特别是 eddyb 的评论 here基本上给出了上面的解释(尽管是在闭包的上下文中,而不是普通函数)。问题本身并没有解决当前的问题。相反,它解决了如果您在使用之前将厕所封闭分配给变量会发生什么(试试看!)。

future 情况可能会发生变化,但这需要对泛型函数的单态化方式进行相当大的改变。

关于rust - 为什么 `std::mem::drop` 与高级特征边界中的闭包 |_|() 不完全相同?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59023616/

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