1. Symptoms
When the Rust compiler encounters error E0387, you will see output similar to the following in your terminal:
error[E0387]: cannot call method `method_name` on a type `dyn TraitName`
--> src/main.rs:12:5
|
12 | obj.method_name();
| ^^^^^^^^^^^^^^^^^ method `method_name` is not available on `dyn TraitName`
|
= note: method `method_name` has no `self` parameter
= note: trait `TraitName` defines an item with this name, but this method cannot be dispatched to a trait object
Another variant of this error appears when you attempt to call a method that exists on the underlying type but is not part of the trait’s interface:
error[E0387]: attempted to call value of method `specific_method` which is not provided by a known trait or this trait object
--> src/lib.rs:45:13
|
45 | trait_obj.specific_method();
| ^^^^^^^^^^^^^
The error manifests in several contexts: when using Box<dyn Trait>, &dyn Trait, &mut dyn Trait, or other trait object types. You may encounter this when working with abstract types in function parameters, return types, or data structures that store trait objects.
2. Root Cause
Error E0387 in Rust arises from the fundamental nature of trait objects and dynamic dispatch. When you create a trait object using dyn Trait, you are erasing the concrete type information and only keeping the trait’s interface. This means the compiler can only guarantee the existence of methods that are explicitly declared in the trait definition.
The underlying cause typically falls into three categories. First, you may be attempting to call an inherent method that exists on the concrete type but was never declared in the trait itself. Since the trait object hides the concrete type, these inherent methods are invisible to the compiler when working through the trait object.
Second, you might be violating object safety rules. A trait is object-safe only if it meets certain criteria: all methods must have a self parameter (either self, &self, or &mut self), and the trait cannot have associated constants or associated types with certain constraints. When a trait is not object-safe, you cannot create a dyn Trait from it, and attempting to call methods that would violate object safety through a trait object triggers E0387.
Third, you may be attempting to call an associated function rather than an instance method. Associated functions like TraitName::new() do not receive a self parameter and therefore cannot be called through a trait object. Only methods that receive a self parameter can be dispatched dynamically.
3. Step-by-Step Fix
The appropriate fix depends on the root cause of the error in your specific situation. Here are the primary solutions:
Fix A: Call the Method on the Concrete Type Before Boxing
If you need to call an inherent method, do so on the concrete type before converting it to a trait object:
Before:
trait Printable {
fn print(&self);
}
struct Document {
content: String,
}
impl Document {
fn save(&self) {
println!("Saving: {}", self.content);
}
}
impl Printable for Document {
fn print(&self) {
self.save(); // This works
println!("Content: {}", self.content);
}
}
fn main() {
let doc = Document { content: String::from("Hello") };
let printable: Box<dyn Printable> = Box::new(doc);
printable.save(); // E0387: save not available on dyn Printable
}
After:
trait Printable {
fn print(&self);
}
struct Document {
content: String,
}
impl Document {
fn save(&self) {
println!("Saving: {}", self.content);
}
}
impl Printable for Document {
fn print(&self) {
self.save();
println!("Content: {}", self.content);
}
}
fn main() {
let mut doc = Document { content: String::from("Hello") };
doc.save(); // Call inherent method on concrete type first
let printable: Box<dyn Printable> = Box::new(doc);
printable.print();
}
Fix B: Add the Method to the Trait Definition
If you need the method to be callable through the trait object, declare it in the trait:
Before:
trait Drawable {
fn draw(&self);
}
struct Circle {
radius: f64,
}
impl Drawable for Circle {
fn draw(&self) {
println!("Drawing circle with radius {}", self.radius);
}
}
fn main() {
let shape: Box<dyn Drawable> = Box::new(Circle { radius: 5.0 });
shape.resize(10.0); // E0387: resize not in trait
}
After:
trait Drawable {
fn draw(&self);
fn resize(&mut self, new_size: f64);
}
struct Circle {
radius: f64,
}
impl Drawable for Circle {
fn draw(&self) {
println!("Drawing circle with radius {}", self.radius);
}
fn resize(&mut self, new_size: f64) {
self.radius = new_size;
}
}
fn main() {
let mut shape: Box<dyn Drawable> = Box::new(Circle { radius: 5.0 });
shape.resize(10.0); // Now works
shape.draw();
}
Fix C: Use Generic Bounded Parameters Instead of Trait Objects
When you need to call methods not in the trait, use static dispatch with generics:
Before:
trait Serializable {
fn serialize(&self) -> String;
}
fn store(obj: Box<dyn Serializable>) {
obj.save_to_file("data.json"); // E0387
}
After:
trait Serializable {
fn serialize(&self) -> String;
}
trait Storable: Serializable {
fn save_to_file(&self, path: &str);
}
fn store<T: Storable>(obj: T) {
obj.save_to_file("data.json");
}
4. Verification
After implementing your fix, verify it works by compiling your project:
cargo build
If the fix is correct, the build should complete without E0387. You can also run your tests to ensure the functionality remains intact:
cargo test
For trait methods added to fix the issue, verify that all implementors of the trait now provide the required method. The compiler will alert you to any missing implementations:
error[E0309]: method `resize` is not implemented for type `Square`
--> src/main.rs:8:1
|
8 | impl Drawable for Square {
| ^^^^^^^^^^^^^^^^^^^^^^^^ missing method `resize`
Ensure your test cases cover the method calls that were previously failing to confirm the fix addresses the original use case. If you refactored to use generics, verify that monomorphization produces the expected behavior for all type arguments.
5. Common Pitfalls
Several frequent mistakes can lead to E0387 or cause confusion during resolution. Understanding these pitfalls will help you avoid them in the future.
Assuming Trait Objects Retain Concrete Type Methods: A common misconception is that dyn Trait preserves access to methods of the underlying type. It does not. Only methods explicitly declared in the trait are accessible. Always verify that the method you want exists in the trait definition before attempting to call it on a trait object.
Forgetting Object Safety Requirements: When designing new traits that you intend to use as trait objects, remember that not all traits can be converted to dyn Trait. Traits with associated constants, associated types with certain bounds, or methods that do not include self cannot be made into trait objects. Design your traits with object safety in mind from the start, or use the #[trait_variant] attribute for async-friendly alternatives.
Confusing Associated Functions with Instance Methods: Static methods (associated functions) like String::from() or Vec::new() cannot be called on trait objects because they lack a self parameter. If you need factory-style construction through a trait, you must declare it as a method with a self return type:
trait Constructable {
fn new() -> Self; // Associated type construction
}
Using self Instead of Self Incorrectly: When adding methods to traits, ensure you use Self (capital S) to refer to the implementing type, not self (lowercase). Mixing these up leads to type mismatches and confusing error messages.
Neglecting the mut Requirement: If a method modifies self but you declared it as &self rather than &mut self, calls through a shared reference (&dyn Trait) will fail. Ensure your method signatures accurately reflect whether they need mutable access to the receiver.
6. Related Errors
E0038: Trait Cannot Be Used for Dynamic Dispatch: This error occurs when you attempt to create a trait object from a trait that is not object-safe. It is closely related to E0387 because both stem from object safety violations. E0038 typically appears when the trait contains associated types, methods without self parameters, or other features that prevent dynamic dispatch. The solution usually involves redesigning the trait or using a different pattern such as enum-based dispatch or generic implementations.
E0277: The Trait Bound Is Not Satisfied: This error appears when you call a method through a trait object but the underlying type does not implement the required trait method. While E0387 focuses on the unavailability of methods on trait objects, E0277 often surfaces when working with generic parameters and trait bounds. Both errors commonly occur when interfacing with trait objects, and E0277 may appear alongside or as a consequence of E0387.
E0596: Cannot Mutably Borrow Immutable Data: When you attempt to call a mutable method on a trait object held behind an immutable reference, you may encounter E0596. This error is related to E0387 in that both prevent certain method calls on trait objects. E0596 specifically deals with borrowing rules, while E0387 addresses method availability. Understanding the distinction helps you choose the correct fix: either change the method signature to use shared references or adjust how you hold the trait object.