Let's decompose it one-by-one.
Box
Box<T>
is a pointer to heap-allocated T
. We use it here because trait objects can only exist behind pointers.
Trait objects
In Box<Fn() + Send + 'static>
, Fn() + Send + 'static
is a trait object type. In future, it will be written Box<dyn (Fn() + Send + 'static)>
to avoid confusion.
Inside dyn
are restrictions to the original type. Box<T>
can be coerced into Box<Fn() + Send + 'static>
only when T: Fn() + Send + 'static
. Therefore, although we don't know the original type, we can assume it was Fn()
and Send
and had 'static
lifetime.
Fn()
This is a trait, just like Clone
or Default
. However, it uses a special syntax sugar.
Fn(A1, ..., An)
is a syntax sugar for Fn<(A1, ..., An), Output=()>
.
Fn(A1, ..., An) -> R
is a syntax sugar for Fn<(A1, ..., An), Output=R>
.
- This syntax sugar also applies to the following traits:
Fn
, FnMut
, FnOnce
, and FnBox
.
So what does Fn
mean? T: Fn(A1, ..., An) -> R
means x: T
is a callable object with arguments A1, ..., An
and return type R
. Examples include function pointers and closures.
Send
Send
means that values of this type can be sent across threads. Since this is an auto trait, it can be specified as the second bounds of dyn
types (trait object types).
'static
bound
In fact, dyn
types (trait object types) must have exactly one lifetime bound. It's inferred when omitted. The inference rule is described in RFC 0192 and RFC 1156. It's basically as follows:
- If explicitly given, use that lifetime.
- Otherwise, it is inferred from the inner trait. For example,
Box<Any>
is Box<Any + 'static>
because Any: 'static
.
- If the trait doesn't have an appropriate lifetime, it is inferred from the outer type. For example,
&'a Fn()
is &'a (Fn() + 'a)
.
- If that even failed, it falls back to
'static
(for a function signature) or an anonymous lifetime (for a function body).
Conclusion
f: Box<Fn() + Send + 'static>
is an owned pointer to a callable value (with the original type unknown and dynamically change) such as closures (with no argument or no return value), which can be sent across threads and lives as long as the program itself.
Box<&i32>
mean that it will allocate the pointer/reference in theheap
and that theborrowed content (i32)
(the data it is pointing to) could well be on stack? and that*b
will give me&i32
and**b
will give 100 (given thatlet m = 100; let b:Box<&i32> = Box::new(&m);
); Not consideringprintln!
here whichautorefs
– Couture