Error

"conflicting implementations of trait" — How to Fix

Fix the 'conflicting implementations of trait' error by removing duplicate trait implementations for the same type in your code or dependencies.

When the compiler refuses to pick a winner

You split your code into modules. You add a trait implementation in lib.rs. Later you refactor and add another implementation in utils.rs. The build fails. The compiler shouts about conflicting implementations. You didn't copy paste anything. You just hit Rust's coherence rule.

The coherence rule in plain words

Rust guarantees that for any combination of a trait and a type, there is exactly one implementation across your entire crate and every dependency you pull in. This guarantee is called coherence. It prevents the compiler from guessing which method to call when you write value.method().

Think of it like a building's fire alarm system. Each zone has exactly one pull station wired to the main panel. If two stations claim to control the same zone, the panel cannot route the signal correctly. Rust's compiler acts as that panel. It maps every trait and type to a single source of truth before it generates machine code. If it finds two sources, it stops the build.

The compiler isn't being difficult. It's protecting you from ambiguous method calls.

A minimal duplicate

The simplest trigger is two impl blocks for the same trait and type in the same crate.

/// Trait that defines a logging interface
trait Logger {
    fn log(&self, message: &str);
}

/// Application state container
struct App;

/// First implementation of Logger for App
impl Logger for App {
    fn log(&self, message: &str) {
        // WHY: Prints to stdout with a standard prefix
        println!("INFO: {}", message);
    }
}

/// Second implementation of Logger for App
impl Logger for App {
    fn log(&self, message: &str) {
        // WHY: Attempts to print with a debug prefix
        println!("DEBUG: {}", message);
    }
}

The compiler rejects this immediately. It does not pick the first one. It does not pick the last one. It refuses to compile.

Merge the blocks. The compiler will thank you.

What happens under the hood

When you run cargo build, the compiler performs name resolution and trait resolution in separate passes. During trait resolution, it builds a global table of all impl Trait for Type blocks. It indexes them by trait ID and type ID. When it encounters a method call like app.log("start"), it looks up Logger and App in that table.

If the table contains two entries for the same key, the compiler cannot generate a single function pointer. Rust does not use virtual dispatch tables for trait objects unless you explicitly use dyn Trait. For static dispatch, the compiler needs to monomorphize the exact function at compile time. Two implementations break monomorphization. The compiler aborts with E0119 (conflicting implementations of trait).

This is a compile time guarantee. There is no runtime overhead to check which implementation wins. The rule exists so that method resolution is deterministic and fast.

Real world collisions

Duplicate blocks in the same file are easy to spot. The real friction comes from conditional compilation, dependencies, and blanket implementations.

Conditional compilation traps

You might write two implementations guarded by feature flags, forgetting that both flags can be enabled simultaneously.

/// Configuration struct for the application
struct Config {
    name: String,
}

/// Standard display implementation
impl std::fmt::Display for Config {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        // WHY: Formats the config for production logs
        write!(f, "Config({})", self.name)
    }
}

/// Debug display implementation
#[cfg(feature = "verbose")]
impl std::fmt::Display for Config {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        // WHY: Formats the config with extra fields for debugging
        write!(f, "Config({:?})", self)
    }
}

If you build with --features verbose, both #[cfg] conditions evaluate to true. The compiler sees two impl Display for Config blocks. The fix is to make the conditions mutually exclusive or merge them into one block with internal if checks.

Blanket implementation overlap

Blanket implementations apply a trait to all types that satisfy a bound. They are powerful for providing default behavior. They also lock you out of specific overrides.

/// Trait for processing data
trait Process {
    fn run(&self);
}

/// Blanket implementation for all types
impl<T> Process for T {
    fn run(&self) {
        // WHY: Provides a fallback for types without custom logic
        println!("Running generic process");
    }
}

/// Specific implementation for String
impl Process for String {
    fn run(&self) {
        // WHY: Attempts to override the blanket impl for String
        println!("Running string process");
    }
}

Rust forbids overlapping implementations. The compiler cannot decide whether my_string.run() should call the generic version or the specific version. You must choose one path. Remove the blanket implementation, or remove the specific one.

Blanket implementations are powerful, but they lock you out of specific overrides. Choose one path.

Pitfalls and compiler signals

The error message is usually E0119. It points to the two conflicting impl blocks. It does not suggest workarounds. It states the rule and stops.

You cannot silence this error with #[allow(dead_code)] or #[allow(unused)]. Coherence violations are hard errors. The compiler treats them the same way it treats type mismatches. The build fails until the conflict is resolved.

Another common trap is dependency version drift. Crate A version 1.0 implements TraitX for MyType. Crate B version 2.0 also implements TraitX for MyType. If your Cargo.toml pulls in both, the compiler sees two implementations from different crates. The fix is to align dependency versions or use cargo tree to trace which crate introduces the duplicate.

Coherence violations are hard stops. You cannot #[allow] your way out of them.

Choosing your resolution path

Pick the pattern that matches your architecture. The compiler will enforce the rest.

Use #[cfg] attributes when you need mutually exclusive implementations based on build targets or feature flags. Guard each block with opposite conditions so only one compiles at a time.

Use the newtype pattern when you want to implement the same trait for two different wrapper types that hold the same underlying data. Wrap the type in a distinct struct, implement the trait separately, and let the compiler treat them as different types.

Use trait renaming when two crates provide conflicting implementations and you cannot control either dependency. Create a local trait with a different name, implement it for your types, and avoid pulling in the conflicting external trait.

Use a single unified impl block when you accidentally split a trait implementation across modules. Rust allows you to split impl blocks across files, but only if they implement different traits or different types. Merge the methods into one block or restructure your modules.

Reach for cfg_attr when you need to apply different attributes to the same implementation without duplicating code. Apply conditional attributes to the methods inside a single impl block instead of duplicating the entire block.

Where to go next