How to call methods that use custom implementation trait?

Viewed 186

Consider this example:

use std::collections::BTreeMap;
trait Map<K, V> {
    fn insert(&mut self, key: K, value: V) -> Option<V>;
}
impl<K: ToString, V> Map<K, V> for BTreeMap<K, V> {
    fn insert(&mut self, key: K, value: V) -> Option<V> {
        println!("{}", key.to_string());
        Some(value)
    }
}
fn main() {
    let mut map = BTreeMap::new();
    let output = map.insert("key", "value");
    // expected output: `Some("value")`
    println!("{:?}", output); // But Got: `None`
}

As you can see I am trying to call a method in Map<K, V> trait, that has a method called insert(..),

Why rust didn't called that method (insert) from Map<K, V> trait, Why It didn't work?

But, If I rename this method in different name, let say set(..), It worked!

3 Answers

You need a fully qualified call in order to disambiguating the call to your implementation:

use std::collections::BTreeMap;
trait Map<K, V> {
    fn insert(&mut self, key: K, value: V) -> Option<V>;
}
impl<K: ToString, V> Map<K, V> for BTreeMap<K, V> {
    fn insert(&mut self, key: K, value: V) -> Option<V> {
        println!("{}", key.to_string());
        Some(value)
    }
}
fn main() {
    let mut map = BTreeMap::new();
    let output = Map::insert(&mut map, "key", "value");
    // expected output: `Some("value")`
    println!("{:?}", output); // But Got: `None`
}

Playground

The question of "how?" was already answered, but I'll try to add a little of "why is it so?"


It's impossible to do in the way you think, because compiler prefers inherent method over trait implementations.

The reasons might be multiple, but one of them is simple: backwards compatibility. If trait methods were to take precedence over inherent ones, the downstream code could suddenly break - or, even worse, silently change behavior! - when someone upstream implements a trait with colliding methods. Consider the following code, for example:

// Let's imagine that this is an external crate.
mod with_trait {
    pub trait Map<K, V> {
        fn insert(&mut self, key: K, value: V) -> Option<V>;
    }
    impl<K, V> Map<K, V> for std::collections::HashMap<K, V> {
        fn insert(&mut self, key: K, value: V) -> Option<V> {
            Some(value)
        }
    }
    // impl<K, V> Map<K, V> for std::collections::BTreeMap<K, V> {
    //     fn insert(&mut self, key: K, value: V) -> Option<V> {
    //         Some(value)
    //     }
    // }
}

// ...and this is your own code.
fn main() {
    use std::collections::{BTreeMap, HashMap};
    use with_trait::Map;

    let mut map = HashMap::new();
    // Maybe you're currently using an implementation for HashMap;
    // here we use a fully-qualified syntax, so that the assertion passes
    assert!(Map::insert(&mut map, "key", "value").is_some());

    let mut map = BTreeMap::new();
    // ...but if trait methods were to take precedence, then
    // by uncommenting `impl Map for BTreeMap`
    // this assertion would be broken!
    assert!(map.insert("key", "value").is_none());
}

Playground

In practice, it's considered minor change to implement a non-fundamental trait, so this potential hazard was reviewed and mitigated.

Just for demonstrate,

@Denys Séguret, said:

"In OOP overriding is done on a subclass, not on the same struct"

Thanks for advice! We could do that:

use std::{collections::HashMap, fmt::Debug, hash::Hash, ops::Deref};
trait X: Debug {}
impl X for &str {}
impl X for bool {}

#[derive(Default, Debug)]
struct Map<K>(HashMap<K, Box<dyn X>>);
impl<K> Deref for Map<K> {
    type Target = HashMap<K, Box<dyn X>>;
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}
impl<K: Eq + Hash> Map<K> {
    // We can now implement our own `insert` funtion!...
    fn insert<V: 'static + X>(&mut self, key: K, value: V) {
        self.0.insert(key, Box::new(value));
    }
}
#[test]
fn main() {
    let mut map = Map::default();
    map.insert("key", "value");
    map.insert("bool", true);

    println!("{:#?}", map);
    assert_eq!(map.len(), 2);
}
Related