Extend a type from 3rd party

I'm using 3rd party crate. There is a type called TreeView. I want to extend that type, so that I have all the functionality the type provides plus my members and fns. How to do it the Rust way?
Thank you

Unlike OOP languages, you do not have inheritance for structs.
You can use composition instead:

struct MyStruct {
   other: OtherStruct,
    // My own fields here.
}

impl MyStruct {
  fn foo(&self) {
     self.other.foo();
  }
  fn bar() {
    // Depends on my members, so manually implement it.
  }
}

First concern:
What if the other type has 10's of fn? Do I really have to wrap them manually or is there other way?

If the struct you are wrapping has dozens of functions and you need all of them, then wrapping is a bad idea in the first place. You could leave it as a pub field (so have pub other: OtherStruct your field), but then the user would have to figure out when to use the inner struct and when to use the outer one.
Secondly, if you have something specific in mind and don't mind sharing it, then you can share it here - you'd get better advice.

I absolutely don't mind sharing.
I'm using(trying to use) this lib:

There is a type called TableView. I need to extend that type so I can have all the functionality of that type + fiels string containing the name of that table.

Well I'd say you're better of filing a PR to this Github repo adding the name field - so that way you don't need to wrap around it.

And that's the problem with rust tbh. Something as trivial as this...
My opinion is such, that not having the possibility to inherit is a huge mistake. I mean seriously... Ehh...

Then just use a language that you like better. Simple, isn't it?

1 Like

So basically, what you're saying is, stop learning rust, do it in other language?

I am not saying anything about what you should do or shouldn't do. But my point is that Rust isn't like other languages and can't be used as such. You should use the language that suits you best, suits your project and works best with the way you think.

You should use Rust if:

  • It is the right language for the job
  • Or, you just like it (like I do)
  • Or you are working on a previous project and so have no option.
    Otherwise, there is no reason to make your life harder.
1 Like

Wait, who said that?

Then just use a language that you like better. Simple, isn't it?

1 Like

If you wrap the 3rd party type in a newtype struct N(TreeView);, and then implement impl std::ops::Deref<Target = TreeView> for N, then you can call all TreeView methods on your N value without having to implement any delegating methods manually.

However it's then your responsibility to make sure that that is desirable and workable.

2 Likes

Sounds like a good idea.
Thanks

Unfortunately it doesn't work in the scenario where I want to add extra struct member. So no go.

Of course it does:

// Imagine that this is an external crate.
mod external {
    pub struct TreeView;
    impl TreeView {
        pub fn base_method(&self) {
            println!("Base method called");
        }
    }
}

use std::ops::Deref;
use external::TreeView;

struct ExtendedTreeView {
    base: TreeView,
    extension: i32,
}

impl Deref for ExtendedTreeView {
    type Target = TreeView;
    fn deref(&self) -> &Self::Target {
        &self.base
    }
}

fn main() {
    let view = ExtendedTreeView { base: TreeView, extension: 0 };
    view.base_method();
    println!("New field: {}", view.extension);
}
3 Likes

Hey, thanks,
I've just figured it out.
The point is to use new type as a member and implement deref on that.
Thanks again.

1 Like

I was actually referring to the "new type" way.
But thanks for the example.

As far as I understand, implementing the Deref trait to simulate inheritance is an anti-pattern in Rust and should be avoided. If you're learning Rust, you might want to keep that in mind. You can have a look at Deref Polymorphism, in particular the Disadvantages section.

At the bottom they mention the delegate crate, which seems to cut down on the work required to implement @RedDocMD's original suggestion when there are a lot of methods you want to wrap. I haven't used the crate myself, and there might be others that suit you better.

2 Likes

The idiomatic way to add new behaviour (but not state) to an existing type is with an extension trait.

trait PrintExt {
    fn print(&self);
}

impl PrintExt for String {
    fn print(&self) {
        println!("Printing: {}", self);
    }
}

fn main() {
    let some_string = String::from("This is a string");
    some_string.print();
}

(playground)

1 Like

Hi and thanks for the info. I really appreciate it.
Tbh, until rust will get proper mechanisms to do inheritance, I am going to use Deref (anti)pattern to achieve my goals when inheritance is concerned. The disadvantages mentioned on that page seem to me non-convincing at all.
As for delegate crate, it still requires you to list all those methods, so what's the point really?