Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to typecast and inherit Rust structs?

Tags:

struct

rust

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;
like image 699
Suraj Avatar asked May 13 '14 06:05

Suraj


3 Answers

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.

like image 73
nrc Avatar answered Oct 28 '22 10:10

nrc


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(),
};
like image 40
Suraj Avatar answered Oct 28 '22 09:10

Suraj


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).

like image 20
Kornel Avatar answered Oct 28 '22 10:10

Kornel