I am learning Rust and have run into the following problem, which is not obvious to me.
I saw a std::thread::spawn in the library, looked at the implementation, and saw that some type requires an implementation of the Send trait to be able to send something to another thread.
I’m trying to replicate the behavior with my own function and my own trait, but the compiler successfully compiles the code without complaining that the trait is not implemented for my types.
What am I missing?
pub trait SomeTrait {
fn bar(&self);
}
#[derive(Debug)]
struct StructWithoutTrait {
_data: Box<i32>
}
#[derive(Debug)]
struct StructWithTrait {
_data: Box<i32>
}
impl SomeTrait for StructWithTrait {
fn bar(&self) {}
}
fn foo<F, T>(f: F) -> T
where
F: FnOnce() -> T,
F: SomeTrait + 'static,
T: SomeTrait + 'static
{
f.bar();
f()
}
impl<F, T> SomeTrait for F
where
F: FnOnce() -> T,
T: SomeTrait
{
fn bar(&self) {}
}
fn main() {
let without_trait = StructWithoutTrait { _data: Box::new(1) };
let with_trait = StructWithTrait { _data: Box::new(2) };
let x = std::rc::Rc::new(1);
foo(move || {
println!("{:?}", without_trait);
println!("{:?}", x);
with_trait
});
}
>Solution :
Send is an auto trait. Auto-traits are somewhat like compiler-implemented; the steps of determining whether a type T implements an auto trait AutoTrait are the following:
- If there is an explicit
impl AutoTrait for T, thenTalways implsAutoTrait(note that becauseSendis anunsafe traityou needunsafe impl Send for T, but the general principle stays the same). - Otherwise, if there is a negative impl
impl !AutoTrait for T, thenTdoes not implementAutoTrait. Note that this is an error to have both positive and negative impl of the same trait for the same type. - Otherwise, if any of the fields of
Tdoes not implementAutoTrait,Tdoes not implement it either. For example, ifTis defined asstruct T(U);, and there’simpl !AutoTrait for U, thenTdoes not implementAutoTrait. - Otherwise,
TimplementsAutoTrait.
Both auto traits and negative impls are highly unstable features, and not intended to use as of now outside of the standard library. If you really want, you can, although because the compiler must be able to implement the trait automatically, it cannot contain any associated item (methods, associated types, associated consts). If you remove the bar() method, this is how your code will look like:
#![feature(auto_traits, negative_impls)]
pub auto trait SomeTrait {}
#[derive(Debug)]
struct StructWithoutTrait {
_data: Box<i32>
}
impl !SomeTrait for StructWithoutTrait {}
#[derive(Debug)]
struct StructWithTrait {
_data: Box<i32>
}
fn foo<F, T>(f: F) -> T
where
F: FnOnce() -> T,
F: SomeTrait + 'static,
T: SomeTrait + 'static
{
f()
}
fn main() {
let without_trait = StructWithoutTrait { _data: Box::new(1) };
let with_trait = StructWithTrait { _data: Box::new(2) };
let x = std::rc::Rc::new(1);
foo(move || {
println!("{:?}", without_trait);
println!("{:?}", x);
with_trait
});
}