为什么`std :: mem :: drop`与排名较高的特征范围中的闭包| _ |()不完全相同? [英] Why isn't `std::mem::drop` exactly the same as the closure |_|() in higher-ranked trait bounds?

查看:103
本文介绍了为什么`std :: mem :: drop`与排名较高的特征范围中的闭包| _ |()不完全相同?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

std :: mem的实现: :drop 记录如下:

The implementation of std::mem::drop is documented to be the following:

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

因此,我希望闭包 | _ | ()(俗称厕所关闭 )在两个方向上可能是 drop 的1:1替代。但是,下面的代码显示 drop 与该函数的参数上绑定的较高特级特征不兼容,而抽水马桶闭合则与之兼容。

As such, I would expect the closure |_| () (colloquially known as the toilet closure) to be a potential 1:1 replacement to drop, in both directions. However, the code below shows that drop isn't compatible with a higher ranked trait bound on the function's parameter, whereas the toilet closure is.

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 的签名,而当把抽水马桶盖代替它时又有什么不同呢?

Considering that drop is supposedly generic with respect to any sized T, it sounds unreasonable that the "more generic" signature fn(_) -> _ is not compatible with for<'a> fn (&'a _) -> _. Why is the compiler not admitting the signature of drop here, and what makes it different when the toilet closure is placed in its stead?

推荐答案

问题的核心是 drop 不是单个函数,而是一组参数化的函数,它们每个掉落某种特定的类型。为了满足更高级别的特征范围(此后称为hrtb),您需要一个 single 函数,该函数可以同时引用具有任何给定生命期的类型。

The core of the issue is that drop is not a single function, but rather a parameterized set of functions that each drop some particular type. To satisfy a higher-ranked trait bound (hereafter hrtb), you'd need a single function that can simultaneously take references to a type with any given lifetime.

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

We'll use drop as our typical example of a generic function, but all this applies more generally too. Here's the code for reference: fn drop<T>(_: T) {}.

概念上, drop 不是单个函数,而是每个 T 可能类型的一个函数。 drop 的任何特定实例仅采用单一类型的参数。这称为单色化。如果对 drop 使用了不同的 T ,则 drop 已编译。这就是为什么您不能将通用函数作为参数传递并完全通用地使用该函数的原因(请参阅这个问题

Conceptually, drop is not a single function, but rather one function for every possible type T. Any particular instance of drop takes only arguments of a single type. This is called monomorphization. If a different T is used with drop, a different version of drop is compiled. That's why you can't pass a generic function as an argument and use that function in full generality (see this question)

另一方面,类似 fn pass(x:& i32)-> & i32 {x} 满足<’a>的hrtb Fn(&’a i32)-> &’a i32 。与 drop 不同,我们有一个单个函数,它同时满足 Fn(&’a i32)-> &’a i32 每个生存期’a 。这反映在通行证的使用方式上。

On the other hand, a function like fn pass(x: &i32) -> &i32 {x} satisfies the hrtb for<'a> Fn(&'a i32) -> &'a i32. Unlike drop, we have a single function that simultaneously satisfies Fn(&'a i32) -> &'a i32 for every lifetime 'a. This is reflected in how pass can be used.

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

(操场)

在该示例中,生存期'a 'b 彼此没有关系:两者都不完全包含彼此。因此,这里没有发生任何子类型化的事情。 pass 的单个实例实际上已用于两个不同的,无关的生命周期。

In the example, the lifetimes 'a and 'b have no relation to each other: neither completely encompasses the other. So there isn't some kind of subtyping thing going on here. A single instance of pass is really being used with two different, unrelated lifetimes.

这就是为什么 drop 对于不满足 FnOnce(&’a T) drop 的任何特定实例只能覆盖 one 个生存期(忽略子类型)。如果我们从上面的示例中将 drop 传递给 two_uses (稍作签名更改并假定编译器允许我们使用),将不得不选择某些特定的生命周期'a drop 范围内的实例two_uses 将是 Fn(&'a i32)一段 concrete 寿命'a 。由于该函数仅适用于单个生命周期'a ,因此无法在两个不相关的生命周期中使用它。

This is why drop doesn't satisfy for<'a> FnOnce(&'a T). Any particular instance of drop can only cover one lifetime (ignoring subtyping). If we passed drop into two_uses from the example above (with slight signature changes and assuming the compiler let us), it would have to choose some particular lifetime 'a and the instance of drop in the scope of two_uses would be Fn(&'a i32) for some concrete lifetime 'a. Since the function would only apply to single lifetime 'a, it wouldn't be possible to use it with two unrelated lifetimes.

那么马桶盖为什么会出现HRTB?推断闭包的类型时,如果预期的类型暗示需要更高级别的特征绑定,请编译器将尝试使之合适。在这种情况下,它会成功。

So why does the toilet closure get a hrtb? When inferring the type for a closure, if the expected type hints that a higher-ranked trait bound is needed, the compiler will try to make one fit. In this case, it succeeds.

问题#41078 与此密切相关,特别是eddyb的评论此处本质上给出了上述说明(尽管在闭包的上下文中,而不是在普通函数中)。问题本身并不能解决当前问题。相反,它解决了在将抽水马桶盖使用前分配给变量(尝试一下!)的情况。

Issue #41078 is closely related to this and in particular, eddyb's comment here gives essentially the explanation above (though in the context of closures, rather than ordinary functions). The issue itself doesn't address the present problem though. It instead addresses what happens if you assign the toilet closure to a variable before using it (try it out!).

将来这种情况可能会改变,但这将需要对泛型函数的单态化进行很大的改变。

It's possible that the situation will change in the future, but it would require a pretty big change in how generic functions are monomorphized.

这篇关于为什么`std :: mem :: drop`与排名较高的特征范围中的闭包| _ |()不完全相同?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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