Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
477 views
in Technique[技术] by (71.8m points)

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

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

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

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

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.

(|_| () (俗称马桶盖 )可以在两个方向上以1:1的比例drop 。)

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.

(但是,下面的代码显示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!
}

The compiler's error message:

(编译器的错误信息:)

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

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 _) -> _ .

(考虑到drop对于任何大小的T都是通用的,听起来“更通用”的签名fn(_) -> _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的签名?当代之以抽水马桶盖代替它时,有什么不同之处?)

  ask by E_net4 says Reinstate translate from so

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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.

(问题的核心在于drop不是单个函数,而是一组参数化的函数,每个函数都删除某些特定类型。)

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.

(为了满足势必更高的排名性状(以下简称hrtb),你需要一个单一的功能,可以同时进行的引用与任何给定的寿命类型。)


We'll use drop as our typical example of a generic function, but all this applies more generally too.

(我们将使用drop作为泛型函数的典型示例,但这也更普遍地适用。)

Here's the code for reference: fn drop<T>(_: T) {} .

(以下是供参考的代码: fn drop<T>(_: T) {} 。)

Conceptually, drop is not a single function, but rather one function for every possible type T .

(从概念上讲, drop不是单个函数,而是每个可能类型T一个函数。)

Any particular instance of drop takes only arguments of a single type.

(drop任何特定实例仅采用单一类型的参数。)

This is called monomorphization .

(这称为单态化 。)

If a different T is used with drop , a different version of drop is compiled.

(如果不同的T与用于drop ,不同版本的drop被编译。)

That's why you can't pass a generic function as an argument and use that function in full generality (see this question )

(这就是为什么您不能将通用函数作为参数传递而不能完全泛泛地使用该函数的原因(请参阅此问题 ))

On the other hand, a function like fn pass(x: &i32) -> &i32 {x} satisfies the hrtb for<'a> Fn(&'a i32) -> &'a i32 .

(另一方面, fn pass(x: &i32) -> &i32 {x}之类的函数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 .

(不像drop ,我们有一个单一的功能,可以同时满足Fn(&'a i32) -> &'a i32 寿命'a 。)

This is reflected in how pass can be used.

(这反映在如何使用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)

((操场))

In the example, the lifetimes 'a and 'b have no relation to each other: neither completely encompasses the other.

(在该示例中,生命周期'a'b彼此之间没有关系:两者都不完全包含彼此。)

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.

(实际上, pass的单个实例实际上具有两个不同的,不相关的生存期。)

This is why drop doesn't satisfy for<'a> FnOnce(&'a T) .

(这就是为什么drop for<'a> FnOnce(&'a T)不满足for<'a> FnOnce(&'a T) 。)

Any particular instance of drop can only cover one lifetime (ignoring subtyping).

(drop任何特定实例只能覆盖一个生命周期(忽略子类型)。)

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 .

(如果我们从上面的示例two_uses drop传递给two_uses (稍作签名更改,并假设编译器允许我们这样做),则必须选择一些特定的生存期'a并且two_uses范围内drop的实例为Fn(&'a i32)保持一定的具体寿命'a 。)

Since the function would only apply to single lifetime 'a , it wouldn't be possible to use it with two unrelated lifetimes.

(由于该函数仅适用于单个生命周期'a ,因此无法将其与两个不相关的生命周期一起使用。)

So why does the toilet closure get a hrtb?

(那么,为什么马桶盖得到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.

(在这种情况下,它成功。)


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

(问题#41078与此密切相关,尤其是eddyb的注释本质上给出了上面的解释(尽管在闭包的上下文中,而不是在普通函数中)。)

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.

(情况将来可能会改变,但是这将需要对泛型函数的单一化方式进行很大的改变。)


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...