`Box`ed 类型的方法调用解析 [英] Method call resolution for `Box`ed type

查看:29
本文介绍了`Box`ed 类型的方法调用解析的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Rust 游乐场:

trait FnBox {fn call_box(self: Box);}impl<F: FnOnce()>FnBox 为 F {fn call_box(self: Box) {(*自己)()}}fn 主(){让作业:Box= Box::new(|| {});//编译的版本//let job = Box::new(|| {});//让作业:Box= Box::new(|| {});job.call_box();}

job.call_box(); 不编译:

<块引用>

error[E0599]: 没有为类型`std::boxed::Box<std::ops::FnOnce()>找到名为`call_box`的方法.` 在当前范围内-->src/main.rs:16:9|16 |job.call_box();|^^^^^^^^^|= 注意:job 是一个函数,也许你想调用它= 注意:方法`call_box` 存在,但不满足以下特征边界:`std::ops::FnOnce() : FnBox`= help: 来自traits的项目只有在trait被实现并且在范围内时才能使用= 注意:以下特征定义了一个项目`call_box`,也许你需要实现它:候选 #1:`FnBox`

为什么编译器在此处看不到到 FnBox 的转换?

我想当我使用 let job = Box::new(|| {}); 时会导致 Box 类型.编译器设法解析 job.call_box(),即 Box -> Box -> Box(因为 FnBox 是为 FnOnce() 实现的,而不是为 closure 实现的).为什么不只是 Box -> Box 工作?

解决方案

作者注:从 Rust 1.35 开始,不需要像 FnBox 这样的适配器特性来调用装箱的 FnOnce 闭包;阅读到最后以了解原因.

FnBox 没有FnOnce() 实现.

FnOnce() 有两个含义:作为特征和作为类型.在 Box 中,它是一个(动态的,未调整大小的)类型.自 Rust 1.27 起,该类型可以更明确地写为 dyn FnOnce(). 对于这个答案的其余部分我将使用 dyn 以便在我谈论特征和动态类型时说清楚.

您编写的 impl 没有为 dyn FnOnce() 类型实现 FnBox,因为 通用参数有一个隐式的Sized绑定.如果我们遵循该问题的答案的建议并将 + ?Sized 添加到 F,编译器确实会给出稍微有用的错误消息:

implFnBox 为 F {fn call_box(self: Box) {(*自己)()}}

error[E0161]: 无法移动 F 类型的值:无法静态确定 F 的大小-->src/main.rs:7:9|7 |(*自己)()|^^^^^^^^

明确指出需要 Sizedness 的地方.

在 1.35 之前的 Rust 版本中,没有办法修复这个错误;FnOnce() 的 trait 对象毫无用处.但是,今天Box实现了FnOnce(),所以你可以像普通闭包一样调用它,不用使用*:

implFnBox 为 F {fn call_box(self: Box) {自己()}}

另见

Rust playground:

trait FnBox {
    fn call_box(self: Box<Self>);
}

impl<F: FnOnce()> FnBox for F {
    fn call_box(self: Box<F>) {
        (*self)()
    }
}

fn main() {
    let job: Box<FnOnce()> = Box::new(|| {});
    // versions that compile
    // let job = Box::new(|| {});
    // let job: Box<FnBox> = Box::new(|| {});
    job.call_box();
}

job.call_box(); doesn't compile:

error[E0599]: no method named `call_box` found for type `std::boxed::Box<std::ops::FnOnce()>` in the current scope
  --> src/main.rs:16:9
   |
16 |     job.call_box();
   |         ^^^^^^^^
   |
   = note: job is a function, perhaps you wish to call it
   = note: the method `call_box` exists but the following trait bounds were not satisfied:
           `std::ops::FnOnce() : FnBox`
   = help: items from traits can only be used if the trait is implemented and in scope
   = note: the following trait defines an item `call_box`, perhaps you need to implement it:
           candidate #1: `FnBox`

Why doesn't the compiler see the conversion to FnBox here?

I suppose when I use let job = Box::new(|| {}); that results in a Box<closure> type. The compiler manages to resolve job.call_box(), which is Box<closure> -> Box<FnOnce()> -> Box<FnBox> (because FnBox is implemented for FnOnce(), not for closure). Why doesn't just Box<FnOnce()> -> Box<FnBox> work?

解决方案

Author's note: Since Rust 1.35, an adapter trait like FnBox is not required to call boxed FnOnce closures; read to the end to see why.

FnBox isn't implemented for FnOnce().

FnOnce() has two meanings: as a trait, and as a type. In Box<FnOnce()>, it's a (dynamic, unsized) type. Since Rust 1.27, the type can be written more explicitly as dyn FnOnce(). For the rest of this answer I will use dyn in order to make clear when I'm talking about the trait and when about the dynamic type.

The impl you wrote doesn't implement FnBox for the type dyn FnOnce(), because generic parameters have an implicit Sized bound. If we follow the advice of that question's answer and add + ?Sized to F, the compiler does give a slightly more helpful error message:

impl<F: FnOnce() + ?Sized> FnBox for F {
    fn call_box(self: Box<F>) {
        (*self)()
    }
}

error[E0161]: cannot move a value of type F: the size of F cannot be statically determined
 --> src/main.rs:7:9
  |
7 |         (*self)()
  |         ^^^^^^^

Which does explicitly call out the place where Sizedness is required.

In versions of Rust before 1.35, there was no way to fix this error; trait objects of FnOnce() were just useless. However, today Box<dyn FnOnce()> implements FnOnce(), so you can call it like a normal closure, without using *:

impl<F: FnOnce() + ?Sized> FnBox for F {
    fn call_box(self: Box<F>) {
        self()
    }
}

See also

这篇关于`Box`ed 类型的方法调用解析的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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