Rust Trait object conversion

后端 未结 2 1470
醉梦人生
醉梦人生 2020-12-11 19:02

The following code won\'t compile due to two instances of this error:

error[E0277]: the trait bound Self: std::marker::Sized is not satis

相关标签:
2条回答
  • 2020-12-11 19:21

    Dynamically sized types can also implement traits. In particular, when you define an object-safe trait, the compiler also defines a dynamically sized type with the same name as the trait, which lets you use object types such as &Component.

    Object types such as &Component or &Any are not just ordinary pointers; they're fat pointers. A fat pointer combines a pointer to the data and another piece of data: for object types, it's a pointer to the vtable; for slices, it's the length of the slice.

    When casting from a regular pointer (e.g. a &Button) to an object type, the compiler statically knows which vtable to put in the fat pointer (e.g. Button's vtable for Any). On the other hand, Rust doesn't support casting from an object type to another object type (e.g. from &Component to &Any), because there's not enough data in an object to initialize the new fat pointer. This is why the compiler adds this note to the error message:

    = note: required for the cast to the object type `std::any::Any + 'static`
    

    There are two ways to fix this:

    1. Require that all types implementing Component be Sized:

      trait Component: Any + Sized {
          fn as_any(&self) -> &Any {
              self
          }
      
          fn as_any_mut(&mut self) -> &mut Any {
              self
          }
      }
      

      This has the consequence that you will not be able to use object types such as &Component or Box<Component> at all.

    2. Make the as_any and as_any_mut methods only available when Self is Sized:

      trait Component: Any {
          fn as_any(&self) -> &Any
              where Self: Sized
          {
              self
          }
      
          fn as_any_mut(&mut self) -> &mut Any
              where Self: Sized
          {
              self
          }
      }
      

      This way, you can still use object types for the trait, but you will not be able to call as_any and as_any_mut on them.

    0 讨论(0)
  • 2020-12-11 19:29

    I found what I consider to be a great solution that didn't require new compiler features.

    pub trait Component {
        // ...
    }
    
    pub trait ComponentAny: Component + Any {
        fn as_any(&self) -> &Any;
        fn as_any_mut(&mut self) -> &mut Any;
    }
    
    impl<T> ComponentAny for T
        where T: Component + Any
    {
        fn as_any(&self) -> &Any {
            self
        }
    
        fn as_any_mut(&mut self) -> &mut Any {
            self
        }
    }
    

    From here, I just change all my APIs to accept ComponentAny instead of Component. Because Any is automatically implemented for any 'static type, ComponentAny is now automatically implemented for any 'static type that implements Component. Thanks to Is there a way to combine multiple traits in order to define a new trait? for the idea.

    0 讨论(0)
提交回复
热议问题