Note: This was asked about a pre-1.0 version of Rust, involving a feature which has since been withdrawn from the language.
I am using the experimental feature feature(struct_inherit)
.
I have the LocalPlayer
and NetPlayer
structs and they both implement the Inputable
trait as well as inherit the player_num
field from the virtual struct Player
. Depending on how a game starts, player_2
in my program can either be a LocalPlayer
or NetPlayer
. Depending on which one, the way that the Inputable
trait is implemented changes.
The compiler won't let me dynamically assign player_2
a type depending on whether it's a NetPlayer
or LocalPlayer
. It complains:
error: mismatched types: expected
~player::LocalPlayer
but found~player::NetPlayer
(expected struct player::LocalPlayer but found struct player::NetPlayer)
It also won't let me typecast NetPlayer
or LocalPlayer
pointers to a Player
pointer. It claims they aren't scalable.
The code sample in question is as follows:
let player1 = box player::LocalPlayer::new(0);
let player2;
if local {
player2 = box player::LocalPlayer::new(1);
} else if hosting {
player2 = box player::NetPlayer::new(1);
player2.host();
} else {
player2 = box player::NetPlayer::new(1);
player2.connect();
}
/* ... Omitted Code ... */
let input = player2.get_input(); // Trait function
The struct implementations are as follows:
pub virtual struct Player {
player_num: uint
}
pub struct LocalPlayer: Player;
pub struct NetPlayer: Player;
Struct inheritance in Rust is very primitive; you probably don't want to use it. There is no subtyping or coercion between inheriting structs in Rust. The feature basically just allows you to save typing if you have a lot of structs with similar fields (it's also future proofing for maybe adding more features around them in the future).
You could make Player
, LocalPlayer
and NetPlayer
traits. You then get the subtyping behaviour you want between them. Alternatively, you could make just LocalPlayer
and NetPlayer
structs and let Player
be a trait. You could even have a PlayerImpl
struct which you could inherit from the way you do currently (if you have a lot of fields to share), but you would need to write independent impls for both structs.
I ended up implementing Player
as an enum:
pub enum Player {
Net(NetPlayer),
Local(LocalPlayer)
}
Every time I call a shared function, I need to do the following:
let input = match player2 {
player::Net(player) => player.get_input(),
player::Local(player) => player.get_input(),
};
Rust's traits are similar to interfaces, and can be composed to emulate a hierarchy of interfaces. They're often an alternative to inheritance:
trait GetInput {
fn get_input(&self);
}
impl GetInput for Player {
fn get_input(&self) {}
}
impl GetInput for NetPlayer {
fn get_input(&self) {}
}
// Fast: interface selected at compile time
fn do_something<T: GetInput>(player: T) {
player.get_input();
}
// Flexible: interface selected at run time (virtual call)
// Use with Box<Player> (see "Trait Objects")
fn do_something_dyn(player: &dyn GetInput) {
player.get_input();
}
However, Rust doesn't have inheritance of data. To have common fields shared between types, you need some alternative DIY solution (e.g. getters/setters in traits or structs with enums).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With