I try to port a java program having many inheritance, any advice?

Hi.

I'm trying to port a java program, which is a bioinfomatics tool, to Rust.

I'm not an experienced programmer, and had some trouble porting another java program having many inheritance.

But the program for me to port now, have many class inheritances too.

I googled it a bit and got ways:

  1. struct having fields having parent struct.
  2. using trait

Could you give me a good approach to this?

Thanks in advance.

There is a lot to say on that topic, so to start I'll refer you to a recent discussion in this forum on the same topic.

You may have already seen the Object-Oriented Programming Features of Rust chapter in the book.

Yes, that's called composition and is something that can work.

But one thing you'll hear repeated often is that it usually best to rethink the design to some degree, rather than try to implement implementation inheritance in Rust, since Rust is truly not designed for that. For example, you could try to flatten the class hierarchy and use shared functions rather than parent classes, or try using enums to hold the data and methods that are specific to the subclasses.

Another thing I would recommend is to try porting and changing the code with the information above in mind, and then post specific examples of things that didn't work out well. The more specific your questions, with code examples, the better answers people will have.

2 Likes

I looked at the java code and it uses inheritance like this:

class A {
//    ... some constants ...
//    ... some fields ...
//    ... some static variables ...
//    ... some methods ...
}

class B extends A {
//    ... some constants ...
//    ... some fields ... (may be initialized using B's associated variable and functions)
//    ... some static variables ... (may be initialized using B's associated variable and functions)
//    ... some methods ... (using B's associated variable and functions)
}

class C extends B {
//    ... some constants ...
//    ... some fields ... (may be initialized using A, B's associated variable and functions)
//    ... some static variables ... (may be initialized using A, B's associated variable and functions)
//    ... some methods ... (using A, B's associated variable and functions)
}

I'm going to try to make structs(e.g. A,B,C) for constant, static, fields and traits(e.g. AT, BT:AT, CT:BT+AT). Is it okay?

Sounds like you're going to mimic inheritance using the composition approach, with traits for each superclass and the traits are implemented by each subclass.

One thing I should have added is that composition works really well as a substitute for a class hierarchy when only the subclasses can access the fields and methods of their superclass. In your example this means only B accesses A, and only C accesses B. In that case you don't need traits and there is not really any inheritance, just simple composition. There is no need for the concept of inheritance.

But if other classes, say X and Y that are outside of the class hierarchy of A/B/C, need to call the methods of the superclasses A and B, then you need traits to mimic inheritance. But not only that, you have to implement the methods of a superclass trait in every descendant subclass by forwarding each call to the same method in its superclass. So if A has a method A::a that is accessible to X and Y via C, then there must be a trait AT with method AT::a implemented by A::a to do the real work of this method, but also B must implement AT and B::a must forwarded to A::a, and also C must implement AT and C::a must forward to B::a. This is what I meant by saying that Rust doesn't support inheritance. It has to be done manually. Here's an example.

That is possible, and I think some people do it that way when implementation inheritance is really needed, in spite of it being tedious and repetitive. As I mentioned, the alternative is to try to modify the structure of the code so that implementation inheritance is not needed. But I know that requires a full understanding of the code you're porting, which may or may not be practical at this phase. If it is not practical, I don't think there is anything wrong with mimicking implementation inheritance to begin with, and later trying to refactor to simplify it. This is what one reply in the other discussion was saying:

Edit:

A compromise (some code has to change, but not the basic design) is for each subclass to have a method that returns its parent. Then you don't need traits and the repetition is reduced. But whenever X or Y wants to call a method in A via C, it has to traverse the parents, which can also be tedious. Here's an example. This is actually just composition but could help in porting code that uses inheritance.

All of this is very artificial because it doesn't take into account the complexity of public and private functions, access to constants, etc. But hopefully it gives you some ideas.

1 Like

You might find it more amenable to Rust if you use has-a relationships instead of is-a. "C has a B" looks like this:

struct B {
    // ...
}

struct C {
    b: B,
}

"C is-a B" looks sort of like this:

trait BExt {
    fn do_b(&self);
}

trait CExt: BExt {
    fn do_c(&self);
}

impl BExt for C {
    fn do_b(&self) { todo!() }
}

impl CExt for C {
    fn do_c(&self) { todo!() }
}

Full-ish example: Rust Playground

2 Likes

Thank you for detailed answer!

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.