Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rust Trait object conversion

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

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

I don't understand why Sized is required in this instance as both &self and &Any are pointers and the operation does not require knowledge of the size of the structure that implements the trait, it only requires knowledge of the pointer itself and the type it is converting from and to, which it will have because &self is generic when implemented inside a trait.

I think this may be an instance of the compiler enforcing unnecessary constraints and I've considered filing an issue with the rust-lang GitHub repo but I figured I should probably see if someone here knows something I don't before I go file an issue.

use std::any::Any;

trait Component: Any {
    fn as_any(&self) -> &Any {
        self
    }

    fn as_any_mut(&mut self) -> &mut Any {
        self
    }
}

The alternative to this is to make as_any() and as_any_mut() required functions for the structs that implement this trait, but for those structures the implementation would always be exactly as displayed here down to each individual character, resulting in several instances of identical boilerplate code.

like image 702
Jake Kiesel Avatar asked Jan 12 '17 02:01

Jake Kiesel


People also ask

What is trait object in Rust?

A trait object is an opaque value of another type that implements a set of traits. The set of traits is made up of an object safe base trait plus any number of auto traits. Trait objects implement the base trait, its auto traits, and any supertraits of the base trait.

What does into () mean in Rust?

The Into trait is simply the reciprocal of the From trait. That is, if you have implemented the From trait for your type, Into will call it when necessary.

Should I implement from or into?

Implementing Intoalways use From . However, in most cases, people use Into to do the conversions, and this will allow that. In almost all cases, you should try to implement From , then fall back to Into if From can't be implemented.

Should I implement from or into Rust?

Implementing Into for conversions to external types in old versions of Rust. It is important to understand that Into does not provide a From implementation (as From does with Into ). Therefore, you should always try to implement From and then fall back to Into if From can't be implemented.


1 Answers

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.

like image 123
Francis Gagné Avatar answered Sep 28 '22 08:09

Francis Gagné