Update: fixed object security rules for version 1.0. Namely, by the value of self the method object is unsafe.
This error occurs due to the security of objects .
To create a feature object from a feature, this feature must be object-safe. A symptom is object-safe if both of these statements are preserved:
- it does not have a
Sized requirement, as in trait Whatever: Sized {} ; - all of its methods are object-safe.
A method is object-safe if both of these statements are true:
These limitations are actually quite natural if you think more about them.
Remember that when values are entered into objects of objects, the actual information of their type is erased, including their size. Therefore, object objects can only be used through a link. References (or other smart pointers, such as Box or Rc ), when applied to feature objects, become "fat pointers" - together with a pointer to a value, they also contain a pointer to a virtual table for that value.
Since object objects can be used only through a pointer, self value methods cannot be called on them - to call such methods you will need the actual value. This was a violation of the object’s security at some point, which meant that traits with such methods could not be trait objects, but even before 1.0 the rules were changed to allow self methods on object objects. However, these methods cannot be called, for the reason described above. There is reason to expect that this restriction will be lifted in the future, as it currently leads to some quirks in this language, for example, the inability to call Box<FnOnce()> closures.
self cannot be used in methods that should be called on object objects precisely because object objects have their actual type, but to call such methods, the compiler needs to know this erased type.
Why static methods cannot be called on object objects, I think it’s obvious: static methods by definition "belong" to the attribute itself, and not to the value, so you need to know the specific type that implements the trait call them. More specifically, regular methods are sent through a virtual table stored inside the object-object, but static methods have no receiver, so they have nothing to send, and for this reason they cannot be stored in the virtual table. Thus, they are unacceptable without knowing the specific type.
General methods of signs cannot be called for another reason, more technical than logical, I think. In Rust, common functions and methods are implemented through monomorphization, i.e. For each instance of a generalized function with a specific set of type parameters, the compiler generates a separate function. For the user of the language, it looks like they call a common function; but at the lowest level, for each set of type parameters there is a separate copy of a function specialized for working with type instances.
Given this approach, in order to call common methods for an object-object, you will need its virtual table to contain pointers to almost all kinds of instances of a universal method for all possible types, which, of course, is impossible, since it will require an infinite number of instances. And therefore, calling common methods of object objects is prohibited.
If Drawable is an external sign, then you are stuck - it is impossible to do what you want, that is, call draw() for each element in a heterogeneous collection. If your drawing set is statically known, you can create a separate collection for each drawable type or, alternatively, create your own enum , which will contain an option for each selected type. Then you can implement Drawable for the enumeration itself, which would be pretty simple.