0

I have Java background and i'm playing with Rust. Here is the conceptual challenge that i know how to solve in Java, but not in Rust at the moment.

I have a product trait and concrete product impl hierarchy. In the tests i need to init it depending on the concrete class. In java i'd just check the class, cast to it and init it as needed. This is how i try to solve it in Rust (and it expectedly fails):

pub trait TProduct {
  fn something();
}

pub trait TProductFactory {
  fn build() -> Box<dyn TProduct>;
}

pub TProduct for ProductType1 {}
pub TProduct for ProductType2 {}
pub TProduct for ProductType3 {}

// in the tests:

let product = factory.build();

// pseudo-code below ---
// if product is ProductType1 {
  (product as ProductType1).setTitle("title1"); // specific init 1
}
// else if product is ProductType2 ... {
  (product as ProductType1).setTitle("title2"); // specific init 2
//}
// ^ How can i have concrete-specific behaviour (eg. init here) ---
// 

In regular conditions (production code) i'd subclass or wrap to have concrete-specific behaviour, but structs can't be extended in Rust. What is the canonical Rust way to achieve it?

PS. I consider having another factory impl or wrapper just to have specific behaviour for the test as an over-kill.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
4ntoine
  • 19,816
  • 21
  • 96
  • 220
  • I would consider it a bad practice to return a product from a factory, then dispatch on the concrete type to rewrite the contents of the given product. One purpose of a factory is to return an abstraction. A redesign would probably not have this problem. However - there are two possible solutions if you need to do it this way: You may return an enum type instead of a trait object. Or you can use the trait [Any](https://doc.rust-lang.org/std/any/trait.Any.html) for this. If you need further hints, consider to complete the example or post a playground link. – CoronA Feb 02 '21 at 06:32
  • 1
    I already mentioned that it's used for the testing only. The "proper" production way is to have a factory wrapper/another impl/etc. I'm looking for a lite way if it exists in Rust or a Rust-way to achieve the same. – 4ntoine Feb 02 '21 at 06:41
  • 1
    Not really. In case of `enum` factory interface depends on all the possible impls as they have to be listed in enum values. This makes impossible to declare factory interface in one module (not "module" in terms of Rust, but more a repo or independent piece of code, eg. jar) and impl in another (eg. another jar). So "abstraction" depends on "impl" here which is even worse. – 4ntoine Feb 02 '21 at 06:55
  • The link I provided doesn't even mention enums... Regardless, there is no way to get a `ProductType1` from a `dyn TProduct` without a little help as shown. You can decorate it with `#[cfg(test)]` so that it doesn't factor in to the non-test codebase, but there's no simple hack around it. – kmdreko Feb 02 '21 at 08:07

0 Answers0