today I have learned that rust does not support covariance over an fn parameter only its return type is covariant. (see rust doc)
Why did I learn about that fact in rust? Because I tried to implement a very simple game where I have separated the logic, the event handling, and the drawing in three distinct functions but all operating on the same vector of players.
If this is not possible what would be the equivalent in rust compared to the c# version be?
In C# this is quite simple Fiddle You can define an interface Y that a class X has to implement and define a corresponding delegate which requires as a parameter an IEnumerable of that Interface Y. Now you can share a List of X between different methods that require only an interface Y.
using System;
using System.Collections.Generic;
public interface Actionable{
void Do();
}
public interface Drawable{
void Draw();
}
public class Player: Drawable, Actionable{
public void Do(){
Console.WriteLine("Action");
}
public void Draw(){
Console.WriteLine("Draw");
}
}
public class Program
{
public delegate void DrawHandler(IEnumerable<Drawable> obj);
public delegate void LogicHandler(IEnumerable<Actionable> obj);
public static void gameloop(DrawHandler draw,LogicHandler action){
List<Player> list = new List<Player>(){
new Player()
};
for(int rounds = 0; rounds < 500; rounds++){
draw(list);
action(list);
}
}
public static void Main()
{
gameloop(
list =>{
foreach(var item in list){
item.Draw();
}
},
list =>{
foreach(var item in list){
item.Do();
}
}
);
}
}
Naive as I am, I tried to do something equivalent to that in rust!
trait Drawable {
fn draw(&self) {
println!("draw object");
}
}
trait Actionable {
fn do_action(&self, action: &String) {
println!("Do {}", action);
}
}
#[derive(Debug)]
struct Position {
x: u32,
y: u32,
}
impl Position {
fn new(x: u32, y: u32) -> Position {
Position { x, y }
}
}
#[derive(Debug)]
struct Player {
pos: Position,
name: String,
}
impl Player {
fn new(name: String) -> Player {
Player {
name,
pos: Position::new(0, 0),
}
}
}
impl Drawable for Player {
fn draw(&self) {
println!("{:?}", self);
}
}
impl Actionable for Player {
fn do_action(&self, action: &String) {
println!("Do {} {}!", action, self.name);
}
}
type DrawHandler = fn(drawables: &Vec<&dyn Drawable>) -> Result<(), String>;
type LogicHandler = fn(actions: &Vec<&dyn Actionable>) -> Result<(), String>;
type EventHandler = fn(events: &mut sdl2::EventPump) -> Result<bool, String>;
fn game_loop(
window: &mut windowContext,
draw_handler: DrawHandler,
event_handler: EventHandler,
logic_handler: LogicHandler,
) -> Result<(), String> {
let mut objects: Vec<&Player> = Vec::new();
objects.push(&Player::new("b".to_string()));
while event_handler(&mut window.events)? {
logic_handler(&objects)?; // Does Not work
window.canvas.clear();
draw_handler(&objects)?; // Does Not Work
window.canvas.present();
::std::thread::sleep(Duration::new(0, 1_000_000_000u32 / 60));
}
Ok(())
}
If this is not possible what would be the equivalent in rust compared to the c# version be?
I accept that this is not possible in rust. I would like to know what instead is used in rust