在Rust中,"Box<Fn() + Send + 'static>"是什么意思?

31

Box<Fn() + Send + 'static> 在 Rust 中是什么意思?

在我阅读高级类型章节时偶然发现了这个语法。 Send 是一个 trait,但是将生命周期(在这种情况下为'static)与 trait(Send)相加意味着什么?另外,Fn() 是什么意思?

2个回答

47

让我们逐个分解它。

Box

Box<T>是指向堆上分配的T的指针。我们在这里使用它,因为特质对象只能存在于指针后面。

Trait objects

Box<Fn() + Send + 'static>中,Fn() + Send + 'static是一种trait object类型。将来,它将被写成 Box<dyn (Fn() + Send + 'static)>以避免混淆。

dyn内部限制了原始类型。只有当T: Fn() + Send + 'static时,Box<T>才可以强制转换为Box<Fn() + Send + 'static>。因此,尽管我们不知道原始类型是什么,但我们可以假设它是Fn()Send并且具有'static生命周期

Fn()

这是一个特质,就像CloneDefault一样。但是,它使用了特殊的语法糖

  • Fn(A1, ..., An)Fn<(A1, ..., An), Output=()>的语法糖。
  • Fn(A1, ..., An) -> RFn<(A1, ..., An), Output=R>的语法糖。
  • 这种语法糖也适用于以下特征:Fn, FnMut, FnOnceFnBox

那么Fn是什么意思呢?T: Fn(A1, ..., An) -> R表示x: T是一个可调用对象,其参数为A1, ..., An,返回类型为R。示例包括函数指针和闭包。

发送

Send表示该类型的值可以在线程间发送。由于这是一个自动特质, 它可以作为dyn类型(trait object types)的第二个限制条件 (trait object types).

'static限定

实际上,dyn类型(trait object types)必须有一个生命周期限定。当省略时会被推断。推断规则在RFC 0192RFC 1156中描述。基本规则如下:

  1. 如果明确给出,使用该生命周期。
  2. 否则,从内部特质中推断。例如,Box<Any>Box<Any + 'static>,因为 Any: 'static
  3. 如果特质没有适当的生命周期,则从外部类型中推断。例如,&'a Fn()&'a (Fn() + 'a)
  4. 如果甚至都失败了,就退回到 'static(对于函数签名)或匿名生命周期(对于函数体)。

结论

f: Box<Fn() + Send + 'static> 是一个拥有指向可调用值(原始类型未知且可动态更改),例如没有参数或没有返回值的闭包的所有权指针,可以跨线程发送并在程序本身存在期间保持活动状态。


非常感谢Masaki的详细解释!一个离题的问题:Box<&i32>是否意味着它将在中分配指针/引用,并且借用的内容(i32)(它指向的数据)可能在堆栈上,而*b将给我&i32**b将给我100(假设let m = 100; let b:Box<&i32> = Box::new(&m););不考虑println!这里的autorefs - soupybionics
@soupybionics 我知道你现在可能不在意了,但我相信是这样的。当然,将引用装箱到堆栈分配的值上没有太多理由,而且在 i32 被删除或移动后,你也无法对该盒子进行太多操作,因为它将无效。 - Hutch Moore
@HutchMoore,是的,这更像是一个假设性问题。 - soupybionics

8

我发现最受欢迎的答案中,'static部分需要更详细的解释。

将底层具体类型称为A

特质对象Box<dyn Fn() + Send + 'static>可以由A的实例构建,这意味着A:Fn() + Send + 'static。也就是说,具体类型A受到'static寿命的限制。

trait bound中对'static的解释:

作为一个特质边界,它意味着该类型不包含任何非静态引用。例如,接收方可以持有该类型,直到他们放弃它为止,它永远不会失效。

重要的是要明白,这意味着任何拥有的数据始终通过'static生命周期绑定传递,但对该拥有数据的引用通常不会这样做。

对于使用任何生命周期作为特质边界的情况,有一个生成式的解释

T:'a表示T的所有生命周期参数必须比'a'更长。例如,如果'a'是无约束的生命周期参数,那么i32:'static和&'static str:'a就是满足的,但Vec<&'a ()>: 'static则不是。

对于我们的情况,A的所有生命周期参数都必须比'static更长,例如

pub struct A<'a> {
   buf: &'a[u8]
}

无法满足 A: 'static 要求。


那么,“静态生命周期”是一件坏事吗?如果我滥用它,是否意味着它们永远不会被释放?还是说Rust即使在“静态生命周期”上,如果对象/引用不再使用,也足够聪明地将其释放? - Rafael Merlin
1
@RafaelMerlin,这并不坏。如果在参考上下文中,它意味着在整个程序生命周期内存在。您还可以使用Box来有意地“泄漏”堆内存。完全控制底层内存生命周期是一件好事。如果您决定过度使用它,那么您需要承担后果。我认为这很公平。 - Izana

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接