首页 > 解决方案 > 为什么在特征实现上发送的特征边界被忽略?

问题描述

Send为什么忽略 trait 实现上的 auto trait的 trait bound?(游乐场(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(())
}

例如,为自定义特征 (X) 使用特征绑定它可以工作:( 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(())
}

更令人困惑的是,使用绑定在函数上的特征可以按预期工作:(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(())
}

看起来自动特征Send(或一般的自动特征)被特殊处理。但是,我还没有找到任何关于此的文档。这是一个错误还是只是我缺乏理解:-)?

标签: genericsrustdereference

解决方案


看看你的游乐场的这个稍微修改过的版本

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(())
}

我们得到了结果:

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

我变了:

  • 添加了一些println!调用,以便我们可以看到类型的 TypeId
  • T: 'static由于TypeId限制,添加了一个要求。这不应该影响我们的回答,因为Rc<i32>i32都是'static

可以看出,T被解析为,i32而不是Rc<i32>。也就是说,用而不是is_send调用。T = i32T = Rc<i32>

这是因为Rc<T>实现Deref<Target = T>. 调用i.is_send()的时候,其实就等价于(*i).is_send(), and*i是 an i32,也就是 a Send。当您使用点运算符调用值的方法时,编译器会尝试执行取消引用,直到满足类型边界。

为了展示这一点,让我们尝试更改RcArc, where Arcimplements Send。您可以看到现在具有与而不是T相同的 TypeId 。这是因为已经满足了界限,不需要进一步的解除引用。Arc<i32>i32ArcT: Send

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 }

推荐阅读