简体   繁体   中英

Why are trait bounds for Send on trait implementations ignored?

Why are trait bounds for auto trait Send on trait implementations ignored? ( Playground(1) )

trait IsSend {
    fn is_send(&self);
}

impl<T: Send> IsSend for T {
    fn is_send(&self) {}
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let i = std::rc::Rc::new(43);
    i.is_send(); // (!!) no compiler error although Rc<...> is not Send
    Ok(())
}

For example using a trait bound for a self defined trait (X) it works: ( Playground(2) )

trait X {}

trait IsSend {
    fn is_send(&self);
}

impl<T: X> IsSend for T {
    fn is_send(&self) {}
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let i = std::rc::Rc::new(43);
    i.is_send(); // (ok) compiler error as Rc<...> does not implement X
    Ok(())
}

Even more confusing, using a trait bound on a function it works as expected: ( Playground(3) )

fn is_send<T: Send>(_s: &T) {}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let i = std::rc::Rc::new(43);
    is_send(&i); // (ok) compiler as Rc<...> is not Send
    Ok(())
}

It looks like the auto trait Send (or auto traits in general) is treated specially. However, I have not found any documentation about this. Is this a bug or just my lack of understanding :-)?

Have a look at this slightly modified version of your playground

use std::any::TypeId;

trait IsSend {
    fn is_send(&self);    
}

impl<T: Send + 'static> IsSend for T {
    fn is_send(&self){
        println!("TypeId of T: {:?}", TypeId::of::<T>());
    }
}

fn main() -> Result<(),Box<dyn std::error::Error>> {
    println!("TypeId of i32: {:?}", TypeId::of::<i32>());
    println!("TypeId of Rc<i32>: {:?}", TypeId::of::<std::rc::Rc<i32>>());

     let i = std::rc::Rc::new(43);
     i.is_send(); // (!!) no compiler error although Rc is not Send
     Ok(())
}

And we have the result:

TypeId of i32: TypeId { t: 13431306602944299956 }
TypeId of Rc<i32>: TypeId { t: 1918842018359854094 }
TypeId of T: TypeId { t: 13431306602944299956 }

I changed:

  • Added a few println! calls so that we can see the TypeId of the types
  • Added a requirement T: 'static , due to TypeId constraints. This should not affect our answer, since both Rc<i32> and i32 are 'static .

It can be seen that T is resolved as i32 instead of Rc<i32> . That is, is_send is called with T = i32 rather than T = Rc<i32> .

This is because Rc<T> implements Deref<Target = T> . When you call i.is_send() , it is actually equivalent to (*i).is_send() , and *i is an i32 , which is a Send . The compiler attempts to perform dereferencing when you use the dot operator to call a method on a value until the type bounds are satisfied.

To show this, let's try changing Rc to Arc , where Arc implements Send . You can see that T now has the same TypeId as Arc<i32> rather than i32 . This is because Arc already satisfies the T: Send bound, and no further dereferencing is required.

use std::any::TypeId;
use std::sync::Arc;

trait IsSend {
    fn is_send(&self);    
}

impl<T: Send + 'static> IsSend for T {
    fn is_send(&self){
        println!("TypeId of T: {:?}", TypeId::of::<T>());
    }
}

fn main() -> Result<(),Box<dyn std::error::Error>> {
    println!("TypeId of i32: {:?}", TypeId::of::<i32>());
    println!("TypeId of Arc<i32>: {:?}", TypeId::of::<Arc<i32>>());

     let i = Arc::new(43);
     i.is_send(); // (!!) no compiler error although Rc is not Send
     Ok(())
}
TypeId of i32: TypeId { t: 13431306602944299956 }
TypeId of Arc<i32>: TypeId { t: 3504454246784010795 }
TypeId of T: TypeId { t: 3504454246784010795 }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM