1. Symptoms
When the Rust compiler encounters error E0390, it produces a diagnostic message that clearly indicates a mismatch between the trait definition and its implementation. The compiler output typically appears as follows:
error[E0390]: method `method_name` has a `&self` declaration in the impl, but not in the trait
--> src/main.rs:10:1
|
5 | fn method_name(&self);
| ------------------- the trait has no `&self` declaration
...
10 | fn method_name(&self) { ... }
| ^^^^^^^^^^^^^^^^^^^^^ the impl has a `&self` declaration
Alternatively, you may encounter the inverse situation where the trait declares &self but the implementation does not:
error[E0390]: method `method_name` has no `&self` declaration in the impl, but not in the trait
--> src/main.rs:10:1
|
2 | fn method_name(&self);
| ------------------- the trait has a `&self` declaration
...
10 | fn method_name() { ... }
| ^^^^^^^^^^^^^^^^^^ the impl has no `&self` declaration
In both cases, the compiler halts compilation and refuses to produce an executable until the mismatch is corrected. You may also notice that IDE features such as autocomplete and type checking become unreliable until the error is resolved.
2. Root Cause
Error E0390 stems from a fundamental violation of Rust’s trait implementation rules. In Rust’s type system, method signatures must be identical between the trait declaration and its implementation, including the presence or absence of the &self parameter. This requirement exists because Rust’s trait system relies on structural typing for method resolution, meaning that the compiler must be able to guarantee that any type implementing a trait will provide exactly the methods specified with exactly the signatures declared.
When you define a trait method without &self, you are declaring a static method or an associated function that does not require an instance of the implementing type. Conversely, when you include &self in the method signature, you indicate that the method requires a reference to an instance. The mismatch occurs when the implementation’s method signature diverges from what the trait declares.
This error commonly arises in several scenarios. First, developers sometimes accidentally add or remove &self during implementation after defining the trait. Second, when refactoring code, the trait and implementation may get out of sync if the developer updates one but forgets the other. Third, in complex generic contexts with trait bounds, subtle differences in how methods are specified can lead to this error. Fourth, when copying examples from documentation or Stack Overflow, slight differences in method signatures may have been introduced during transcription.
The Rust compiler enforces this rule strictly because allowing mismatched signatures would break polymorphism. If a method is declared as fn foo() in the trait but implemented as fn foo(&self) in the impl, code that calls trait_object.foo() expecting no receiver would fail at runtime if a different implementation happened to not require &self. The compiler prevents this entire class of bugs at compile time.
3. Step-by-Step Fix
Resolving E0390 requires ensuring that the method signature in your implementation exactly matches the declaration in your trait. The specific fix depends on which direction the mismatch occurs.
Before:
trait Drawable {
// Trait declares a static method without &self
fn new() -> Self;
}
struct Circle {
radius: f64,
}
impl Drawable for Circle {
// ERROR: Implementation uses &self but trait does not
fn new(&self) -> Self {
Circle { radius: 1.0 }
}
}
After:
trait Drawable {
fn new() -> Self;
}
struct Circle {
radius: f64,
}
impl Drawable for Circle {
fn new() -> Self {
Circle { radius: 1.0 }
}
}
Before:
trait Serializable {
// Trait declares an instance method with &self
fn serialize(&self) -> String;
}
struct Data {
value: i32,
}
impl Serializable for Data {
// ERROR: Implementation omits &self
fn serialize() -> String {
String::new()
}
}
After:
trait Serializable {
fn serialize(&self) -> String;
}
struct Data {
value: i32,
}
impl Serializable for Data {
fn serialize(&self) -> String {
format!("Data {{ value: {} }}", self.value)
}
}
Before:
trait Processable {
// Trait declares &mut self for mutation
fn process(&mut self);
}
struct Handler {
count: usize,
}
impl Processable for Handler {
// ERROR: Implementation uses immutable &self
fn process(&self) {
// self.count += 1; // Cannot modify
}
}
After:
trait Processable {
fn process(&mut self);
}
struct Handler {
count: usize,
}
impl Processable for Handler {
fn process(&mut self) {
self.count += 1;
}
}
When fixing this error, always examine both the trait definition and the impl block side by side. Copy the method signature exactly from one to the other to ensure they match precisely, including the presence or absence of &self and &mut self, parameter types, return types, and where clauses.
4. Verification
After applying the fix, verify that the error has been resolved by running the Rust compiler again. Use cargo build or rustc to check for compilation success:
cargo build
A successful build will produce output indicating no errors:
Compiling your_project v0.1.0 (path/to/project)
Finished dev [unoptimized + debuginfo] target(s) in 0.52s
Additionally, create a test case that actually calls the method to ensure the implementation behaves correctly at runtime:
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_trait_method_works() {
let data = Data { value: 42 };
let serialized = data.serialize();
assert!(serialized.contains("42"));
}
#[test]
fn test_static_method_works() {
let circle = Circle::new();
assert_eq!(circle.radius, 1.0);
}
}
Run the tests to confirm functionality:
cargo test
You should see all tests pass, confirming that the trait method signature now matches the implementation and behaves as expected. For more complex scenarios involving generics or associated types, consider adding integration tests that exercise the trait through trait objects or generic bounds.
5. Common Pitfalls
Several common mistakes can lead to persistent E0390 errors or introduce new issues while fixing them. First, developers often confuse the self parameter syntax. Remember that &self is shorthand for self: &Self, &mut self means self: &mut Self, and the absence of self means self by value, which transfers ownership. Each variant must match exactly between trait and impl.
Second, when using generic traits with trait bounds, ensure that the method signature is consistent across all instantiations. If you define a default implementation in the trait that uses &self, the impl that provides a different signature will conflict.
Third, be cautious when implementing multiple traits on the same type. It is easy to accidentally copy the wrong method signature when you have several impl blocks open simultaneously. Use IDE features or code folding to work with one impl block at a time to reduce cognitive load and error probability.
Fourth, when updating a trait definition, remember that all implementations of that trait must be updated simultaneously. A partial update where only some impls are corrected will still result in compilation failures for the remaining implementations.
Fifth, if you are using procedural macros that generate trait implementations, the macro may be generating incorrect signatures. In such cases, examining the macro output with cargo expand can help identify whether the macro is producing the expected code.
Sixth, in nightly Rust with certain unstable features, method signatures can be affected by attributes like #[fundamental] or #[const]. Ensure these modifiers are present on both the trait declaration and the impl if they are present on either.
6. Related Errors
E0050: This error occurs when the method’s parameter list does not match between the trait and implementation. While E0390 specifically targets the &self parameter, E0050 covers mismatches in regular parameters. The root cause is similarβboth errors stem from implementation signatures not matching trait declarations.
E0051: This error appears when you attempt to use a trait method that has a provided default implementation but also has a different signature in the impl. It often accompanies E0390 when developers modify both the trait and impl but introduce subtle differences.
E0183: The trait method has an unknown number of method arguments. This error occurs when the argument count or argument types differ between trait and implementation, which can manifest alongside E0390 when multiple aspects of the method signature are incorrect.
Understanding these related errors helps developers recognize patterns in trait implementation mistakes. In each case, the solution involves carefully comparing the trait declaration with its implementation and ensuring exact correspondence across all aspects of the method signature, including the self parameter, other parameters, return type, and any generic bounds or where clauses.