2.6.1.
Object Safety When defining a trait, whether it is object-safe is also part of the unstated contract.
Object safety is a concept in Rust related to trait objects.
It determines whether a trait can be dynamically dispatched, that is, whether it can be used in the form of .
Traits That Are Object-Safe Must Satisfy the Following Conditions (Based on RFC 255) All supertraits must also be object-safe If a trait inherits from other traits, then those supertraits must also be object-safe.
It must not require A trait cannot use as a supertrait, meaning it cannot contain a bound, because the size of a trait object is unknown at compile time.
It cannot have associated constants.
It cannot have associated types with type parameters.
All associated functions (methods) must satisfy one of the following rules: Dispatchable functions: They cannot have any type parameters, though lifetime parameters are allowed.
They must be methods, and may only appear in receiver positions such as: (where is one of the types above) They cannot require , otherwise the trait would only be usable for types with known size and object safety would be broken.
Explicitly non-dispatchable functions: They may return , but such functions must require , so they cannot be called on trait objects and can only be used with concrete types.
If you cannot remember all of the above, just remember object safety describes whether a trait can be safely turned into a trait object.
What Object Safety Does If a trait is object-safe, meaning it satisfies all of the conditions above, then we can use to treat different types that implement the trait as a single generic type.
If it is not object-safe, the compiler will prevent you from using .
Object Safety and API Design When designing APIs, it is recommended to make traits object-safe, even if that slightly reduces convenience, because it provides new ways to use the trait and increases flexibility.
Let's look at an example: Suppose we have an trait with two methods: and .
The method returns and represents the animal's name.
The method prints an onomatopoeic sound for the animal and returns nothing.
We have two structs, and , and both implement this trait.
The trait is object-safe because it does not return or use generic parameters So we can use it to create a trait object: , and this effectively becomes a trait-object collection Output: Next, let's make a small change to the previous example: We add a new method to the trait, and it returns a value Output: After adding , is no longer object-safe because violates the rule that the return type cannot be .
A is called through a pointer, while refers to the concrete implementation type, whose size is unknown at compile time.
For example: If I want to keep object-safe while also keeping the method, what should I do?
Going back to the first section of this article, look at explicitly non-dispatchable functions: they may return , but such functions must require , so they cannot be called on trait objects and can only be used with concrete types.
According to that requirement, we can change the code like this: Output: That way, there is no error.
Note that can now only be called on concrete types; otherwise it will fail: Output: Because the trait method declares a requirement on the return value, and does not have a known concrete size, the method cannot be called.
Of course, it definitely works on a concrete type: Generic Trait Methods and API Design Put Generic Parameters on the Trait If a trait must have a generic method, consider putting the generic parameter on the trait itself.
Example: There is a trait called , and it has a method called .
The implementation of will definitely need a generic parameter.
But to preserve object safety, we cannot add type parameters to the method itself.
So we move the generic parameter to the trait rather than to the trait method, namely , where is the generic parameter In this way, we can implement the trait for different container types, and