5 ms·
What is the idiomatic way to mimic inheritance in Rust, when writing bindings for OO languages? I'm new to Rust but I imagine there are ways to mimic upcasting/
by xvedejas 5y ago
What is the idiomatic way to mimic inheritance in Rust, when writing bindings for OO languages? I'm new to Rust but I imagine there are ways to mimic upcasting/downcasting its types?
- rav 5y agoYou can take a look at how the Gtk bindings handle the inheritance hierarchy in Gtk objects: https://gtk-rs.org/docs-src/tutorial/object_oriented https://gtk-rs.org/docs-src/tutorial/object_oriented
- andrewzah 5y agoWith the 'as' keyword you can cast between types [0]. Instead of inheritance, you can implement Traits for objects [1], which works more like composition. [0]: https://doc.rust-lang.org/reference/expressions/operator-expr.html#type-cast-expressions https://doc.rust-lang.org/reference/expressions/operator-exp... [1]: https://doc.rust-lang.org/rust-by-example/trait.html https://doc.rust-lang.org/rust-by-example/trait.html
- ChrisSD 5y agoNote that `from` and `try_from` are generally preferred to using `as`, where possible. This also allows casting between non-primitive types. https://doc.rust-lang.org/std/convert/trait.TryFrom.html#examples https://doc.rust-lang.org/std/convert/trait.TryFrom.html#exa...
- xvedejas 5y agoHow expensive is this kind of casting in Rust? Like, if I have a struct which has a superset of attributes that I need for the type I'm casting to, do I need to construct a new struct instance just to have something to treat as my target type, or is there a simpler way that doesn't involve new allocation?
- msbarnett 5y agoYou have options. Given Superset struct A, some other struct B that happens to have a subset of A’s fields, and some function foo which takes an argument of type B, you could 1) implement From<A> for B, and use that to convert instances of A when you want to call foo. This would probably involve some copying and/or allocation. 2) back up and turn foo into something that takes a trait argument instead. Now instead of taking instances of A, what it needs struct to provide is defined by a trait, which you can provide implementations of for both A and B. Now you can pass either to foo. 3) potentially, depending on the layout of structs, create a union and do some type punning to convert between them. this requires unsafe and you’d better be right about the struct layouts. 4) just make foo take a tagged union of the two structs. this isn’t unsafe but has different storage tradeoffs. also foo would need to handle each case separately. 5) find some other way to skin this cat. there’s plenty #2 would be idiomatic in most circumstances I think?
- rackjack 5y agoYeah, I would do (2) if possible. ``` struct Person { name: String, } struct Engineer { name: String, boss: String, } trait Name { fn get_name(&self) -> &str; } impl Name for Person { ... } impl Name for Engineer { ... } ```
- kd5bjo 5y ago6) Instead of having common fields, make A contain an instance of B. Then, implement `AsRef<B> for A`.
- herbstein 5y ago> How expensive is this kind of casting in Rust? It really depends on how you're implementing it. If you're doing a clone/deepcopy-style conversion, or if you're consuming the value you're casting from. However, since most values in Rust are placed on the stack, "allocating" them is incredibly cheap. Moving them around is often also cheap, and involves just a few `mov`s from registers to registers, or stack-address to stack-address. A very simple example: https://godbolt.org/z/WMePqMd71 https://godbolt.org/z/WMePqMd71
- bluejekyll 5y agoI actually end up generally only using polymorphism in Rust and have never needed inheritance. For polymorphism, as other comments have mentioned, you should look at trait objects (`&dyn T`) or generic parameters for monomorphism. If absolutely necessary, Objects can be downcast by using Any (though I've personally not needed to write Rust code that needs this): https://doc.rust-lang.org/std/any/trait.Any.html https://doc.rust-lang.org/std/any/trait.Any.html. Edit, instead I tend to rely on Rust enums (for it's algebraic type features). Also, in some cases you may end up wanting to implement Deref or DerefMut, https://doc.rust-lang.org/std/ops/trait.Deref.html https://doc.rust-lang.org/std/ops/trait.Deref.html, but this shouldn't be use to create inheritance, it's more used for getting references from one type to another similar type (like String derefs to &str for example).