1

There's a library which provides a generic function and some implementations to work with:

#include <iostream>

namespace lib {
  struct Impl1 {};
  struct Impl2 {};

  void process(Impl1) { std::cout << 1; }
  void process(Impl2) { std::cout << 2; }

  template<typename T> void generalize(T t) { process(t); }
}

I want to extend it via external code. Here's how C++ allows to do it:

#include <lib.h> // the previous snippet

namespace client {
  struct Impl3 {};

  void process(Impl3) { std::cout << 3; }
}

int main() { // test
  lib::generalize(client::Impl3{}); // it couts 3
}

Note: lib's code knows nothing about the client's one and performs no dynamic dispatch nonetheless. How can I achieve the same in my Rust code? (If I can't, is anything alike planned?)

passing_through
  • 1,778
  • 12
  • 24
  • BTW can I somehow do this in languages like C# or Java? – passing_through Aug 17 '20 at 11:27
  • 1
    This is a rather misleading example (that is, it leads you away from the solution). You should always try to find an example in the basic functionality of the standard library, because you are likely to find similar functionality in the standard library of the other language. Consider `std::sort` of a container of objects with user-defined `operator<`. Exactly like in your artificial example, `std::sort` knows nothing about your types and doesn't perform dynamic dispatch, yet is perfectly capable of calling your `operator<`. How does Rust cope with this? – n. m. could be an AI Aug 17 '20 at 12:08

2 Answers2

5

Sure, this is exactly what traits are for:

pub mod lib {
    pub trait Impl {
        fn process(&self);
    }

    pub struct Impl1 {}
    pub struct Impl2 {}

    impl Impl for Impl1 {
        fn process(&self) {
            println!("1");
        }
    }

    impl Impl for Impl2 {
        fn process(&self) {
            println!("2");
        }
    }

    pub fn generalize<T: Impl>(t: T) {
        t.process();
    }
}

mod client {
    pub struct Impl3 {}

    impl super::lib::Impl for Impl3 {
        fn process(&self) {
            println!("3");
        }
    }
}

fn main() {
    lib::generalize(client::Impl3 {});
}

See it on the Playground.

eggyal
  • 122,705
  • 18
  • 212
  • 237
1

Rust is stricter, and would possibly require lib to play nice. It could do so by defining a trait that defines that something supports process:

trait Processable {
  fn process(self);
}

struct Impl1 {}
impl Processable for Impl1 {
  fn process(self) {/*TODO*/}
}

fn generalize<T: Processable>(t: T) { t.process(); }

Then, Processable could be used by "outsiders" to inform the system that Impl3 satisfies the required interface:

struct Impl3 {}
impl Processable for Impl3 {
  fn process(self) {/*TODO*/}
}

Then, generalize can also be called for Impl3

phimuemue
  • 34,669
  • 9
  • 84
  • 115