Why doesn't Rust support trait object upcasting?

前端 未结 3 776
名媛妹妹
名媛妹妹 2020-11-22 07:34

Given this code:

trait Base {
    fn a(&self);
    fn b(&self);
    fn c(&self);
    fn d(&self);
}

trait Derived : Base {
    fn e(&sel         


        
相关标签:
3条回答
  • 2020-11-22 07:49

    As of Jun 2017, the status of this "sub-trait coercion" (or "super-trait coercion") is as follows:

    • An accepted RFC #0401 mentions this as a part of coercion. So this conversion should be done implicitly.

      coerce_inner(T) = U where T is a sub-trait of U;

    • However, this is not yet implemented. There is a corresponding issue #18600.

    There is also a duplicate issue #5665. Comments there explain what prevent this from being implemented.

    • Basically, the problem is how to derive vtables for super-traits. Current layout of vtables is as follows (in x86-64 case):
      +-----+-------------------------------+
      | 0- 7|pointer to "drop glue" function|
      +-----+-------------------------------+
      | 8-15|size of the data               |
      +-----+-------------------------------+
      |16-23|alignment of the data          |
      +-----+-------------------------------+
      |24-  |methods of Self and supertraits|
      +-----+-------------------------------+
      
      It doesn't contain a vtable for a super-trait as a subsequence. We have at least to have some tweaks with vtables.
    • Of course there are ways to mitigate this problem, but many with differing advantages/disadvantages! One has a benefit for the vtable size when there is a diamond inheritance. Another is supposed to be faster.

    There @typelist says they prepared a draft RFC which looks well-organized, but they look like disappeared after that (Nov 2016).

    0 讨论(0)
  • 2020-11-22 07:55

    I ran into the same wall when I started with Rust. Now, when I think about traits, I have a different image in mind than when I think about classes.

    trait X: Y {} means when you implement trait X for struct S you also need to implement trait Y for S.

    Of course this means that a &X knows it also is a &Y, and therefore offers the appropriate functions. It would require some runtime-effort (more pointer dereferences) if you needed to traverse pointers to Y's vtable first.

    Then again, the current design + additional pointers to other vtables probably wouldn't hurt much, and would allow easy casting to be implemented. So maybe we need both? This is something to be discussed on internals.rust-lang.org

    0 讨论(0)
  • 2020-11-22 07:59

    Actually, I think I got the reason. I found an elegant way to add upcasting support to any trait that desires it, and that way the programmer is able to choose whether to add that additional vtable entry to the trait, or prefer not to, which is a similar trade-off as in C++'s virtual vs. non-virtual methods: elegance and model correctness vs. performance.

    The code can be implemented as follows:

    trait Base: AsBase {
        // ...
    }
    
    trait AsBase {
        fn as_base(&self) -> &Base;
    }
    
    impl<T: Base> AsBase for T {
        fn as_base(&self) -> &Base {
            self
        }
    }
    

    One may add additional methods for casting a &mut pointer or a Box (that adds a requirement that T must be a 'static type), but this is a general idea. This allows for safe and simple (although not implicit) upcasting of every derived type without boilerplate for every derived type.

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