So I'm trying to make sure I have a proper understanding of Send
and Sync
, and whether they're special cased by the compiler, or if I could do it myself for some other type to make similar guarantees. I can't seem to find confirmation in the docs, so I'm just hoping to state my understanding of everything here and have anything that's incorrect or incomplete pointed out.
So thread::spawn
has the type bound where F: FnOnce() -> T, F: Send + 'static
(side note: Why 'static
? Are there any Fn
types that wouldn't be 'static
?). thread::spawn
doesn't need to mention or know about Sync
, because there is impl<'a, T> Send for &'a T where T: Sync
so the only way to have an aliasable reference that is Send
is for the type it's referencing to be Sync
.
Now where I'm starting to get fuzzy is why FnOnce() -> T
implements Send
. I'm guessing it's because there's impl Send for .. { }
, so the function would automatically implement it if all of its component parts implement it. However, looking at the source of the Fn
traits, I don't see anything that indicates what's part of it. There's the #[rustc_paren_sugar]
annotation, and while I can infer what that does, it doesn't imply what it's actually deriving. I'm assuming that it involves the Args
type parameter. I also don't see where or how the type of the function involves data that it closes over.
However, if I gloss over the details, I'm assuming that means that Fn
includes all variables that it closes over as part of it's members, which would mean that they would be used to determine if that Fn
is Send
or not. That seems to be confirmed by this experiment:
#![feature(optin_builtin_traits)]
unsafe trait Foo {
}
unsafe impl Foo for .. { }
impl !Foo for u16 { }
fn call_foo<F, T>(f: F) -> T where F: Fn() -> T, F: Foo {
f()
}
fn main() {
let a: u8 = 1;
let b: u16 = 2;
println!("{}", call_foo({|| a }));
println!("{}", call_foo({|| b }));
}
The compiler error also seems to confirm this:
foo.rs:18:20: 18:28 error: the trait `Foo` is not implemented for the type `u16` [E0277]
foo.rs:18 println!("{}", call_foo({|| b }));
While having it point out that the problem is u16
is helpful, it also doesn't help me confirm if the actual source of the problem is that the derived Fn
trait for the closure doesn't implement Foo
because u16
doesn't implement it. I also just realized that since u16
is part of the return type, that doesn't actually confirm that closed over variables are part of this as well, but I'm assuming they are?
Is all of this more or less correct? Is there explicit documentation on this somewhere that I'm missing?