如何理解返回另一个函数的Rust函数?

4 rust

阅读有关Rust的内容时,我遇到了一个带有数字的示例函数,并返回一个函数,该函数将该数字添加到另一个数字.

fn higher_order_fn_return<'a>(step_value: &'a i32) -> Box<Fn(i32) -> i32 + 'a> {
    Box::new(move |x: i32| x + step_value)
}
Run Code Online (Sandbox Code Playgroud)

这里有很多特定于Rust的机制,我无法理解它.我敢肯定其中一些与生命周期管理有关,但必须以这种方式编写的原因使我无法理解.几个问题:

  • 为什么step_value传入作为参考?
  • 为什么要返回的函数被装箱?
  • 如何解释编写函数类型的非常规方法(as Fn(i32) -> i32 + 'a)?
  • 为什么'a写为generic(<'a>)但在返回类型(+ 'a)中"添加" ?
  • 移动到底是什么意思move

DK.*_*DK. 6

禁止提出不止一个问题的禁令,但由于这些都属于"这段代码意味着什么",我不会抱怨.此外,它确实发生了不少古怪的压缩到一个相对较小的,不是非常不寻常的片段.

为什么step_value传入作为参考?

不知道.它就是.它可以通过值传递,而不会显着改变代码的语义.但它通过引用传递,这所有其他与生命相关的问题的原因.

为什么要返回的函数被装箱?

它没有返回一个函数.函数定义为fn.它正在关闭.问题在于,出于性能原因,每个闭包实际上都是匿名类型的实例(有时称为"Voldemort类型").匿名类型是一个问题,因为您无法命名它们,但您必须命名您的返回类型.

解决这个问题的方法是返回一个特征对象.在这种情况下,它返回一个Fn.还有FnMutFnOnce.它返回它的盒装因为裸露的特质对象不能被周围按值传递,所以特质对象总是要落后于某种指针(是Box,&,Rc,).

它们不能通过值传递,因为编译器无法确定它有多大,这使得它们几乎不可能移动它们.在那之后,逻辑系列直接转向"编译器如何实现"领域,这在某种程度上超出了范围.

如何解释编写函数类型的非常规方法(as Fn(i32) -> i32 + 'a)?

关于它没有什么不同寻常的.不管怎样,不是Rust,因为这是在Rust中,其他语言的其他方式与之无关.

让我们+ 'a暂时忽略它,因为那实际上是别的东西.这Fn(i32) -> i32是重要的部分.拉斯特每一个"可赎回"的东西实现一个或多个的Fn,FnMutFnOnce特质,这是除锈如何表达的能打电话的东西的想法.parens中的东西是参数,后面的东西->是返回类型,就像函数一样.

您可以在"何时关闭实现Fn,FnMut和FnOnce?"这一问题中了解有关这些特征的更多信息..

为什么'a写为generic(<'a>)但在返回类型(+ 'a)中"添加" ?

首先,因为生命周期是类型系统的一部分.因此,它们进入通用参数列表(内部的东西<...>).

其次,因为编译器必须了解其中的特征对象有多长时间Box才有效.如果有Box<SomeTrait>,编译器允许该值存在多长时间?通常,该信息将是该类型的一部分,但如果您使用的是特征,则编译器不知道正在使用哪种类型.记住,你可以做Box<SomeTrait>出来的任何 Box<T>地方T器具SomeTrait.

在这种情况下,关闭将继续保留step_value借款,这意味着它不得超过借款的生命周期(即'a).但如果类型只是 Box<Fn(i32) -> i32>,编译器就不会有这些信息.因此,有一种语法可以指定无论隐藏在特征对象后面的类型是什么,它都不能超过给定的生命周期.

+ 'a就是所说的:"这是一个实现Fn(i32) -> i32特征的盒装价值,它不能超过生命周期'a".

移动的意义是什么,移动到底是什么?

通常情况下,编译器会尝试猜测它必须做什么才能使闭包工作,但它并不能始终正确.在可能的情况下,它会尝试借用闭包捕获的东西.所以当你step_value在闭包内部使用时,编译器通常会借用它.

这不会是一个问题,除了你将关闭退出函数.这种自动借用仅持续功能的寿命,这不够长.要解决此问题step_value,您可以其移至闭包中,而不是借用.

你可能想知道的好处.

如果你不写+ 'aBox<Trait + 'a>,通常会发生什么?

实际上,编译器在这里有一个启发式.默认情况下,每个特征对象都有一个附加的生命周期.它是从包装它的指针继承而来的.所以,&'a Trait真的&'a (Trait + 'a). Box没有自己的生命周期参数,因此它得到'static( Box<Trait>Box<Trait + 'static>),这意味着默认情况下,盒装特征对象不能包含任何非'static借用.