What is the alternative of downcasting a trait object in this situation? - rust

I have a Companion trait that encompasses a base trait for Components such as Health. I store a list of Companion using trait objects because all companions must at least implement the Companion trait. However not all companions will use the subtype Health trait.
Now the heal command only accepts a list of Health traits, so I need to filter out, remap and downcast all the base Companion traits so that it supports the Health traits.
I understand this is bad design. How can I implement the same behavior without having to downcast the trait objects to a specific component? Note: I cannot have a large struct which includes all the subtypes into one type.
Here's the code I have so far:
type CompanionId = Uuid;
fn main() {
let mut companion_storage: HashMap<CompanionId, Box<dyn Companion>> = HashMap::new();
let companion_id: CompanionId = Uuid::new_v4();
companion_storage.insert(
companion_id,
Box::new(Cheetah {
...
}),
);
let mut player = Player {
...,
companions: Vec::new(),
};
player.companions.push(companion_id);
'GameLoop: loop {
let input = poll_input().trim().to_lowercase();
match input.as_str() {
// TODO: Extract healing component here.
"heal" => heal_command(companion_id, companion_storage.into_iter().filter(|(companion_id, companion)| {
// QUESTION: How do I filter out Companions without the Health trait here so they can automatically be downcasted and mapped?
}).collect()),
"q" => {
break 'GameLoop;
}
"s" => {
status_command(&player, &companion_storage); // SAME PROBLEM HERE
}
_ => println!("Unknown command"),
}
}
}
struct Player {
id: u8,
name: String,
companions: Vec<CompanionId>,
}
trait Companion {
...
}
trait Health: Companion {
...
}
trait Status: Health {}
struct Cheetah {
id: CompanionId,
name: String,
current_health: f32,
max_health: f32,
}
impl Companion for Cheetah {
...
}
impl Health for Cheetah {
...
}
fn heal_command(
companion_id: CompanionId,
companion_storage: &mut HashMap<CompanionId, Box<dyn Health>>,
) {
let companion = companion_storage.get_mut(&companion_id).unwrap();
companion.heal_max();
println!("Healed to max.");
}
fn status_command(player: &Player, companion_storage: &mut HashMap<CompanionId, Box<dyn Status>>) {
println!("Status for {}: ", player.name);
println!("===============================");
print!("Companions: ");
for companion_id in &player.companions {
let companion = companion_storage.get(companion_id).unwrap();
print!(
"{} [{}/{}], ",
companion.name(),
companion.health(),
companion.max_health()
);
}
println!();
println!("===============================");
}
Is this code a better alternative?
type CompanionId = Uuid;
fn main() {
let mut companion_storage: HashMap<CompanionId, Companion> = HashMap::new();
let companion_id: CompanionId = Uuid::new_v4();
companion_storage.insert(
companion_id,
Companion {
id: companion_id,
name: "Cheetah".to_string(),
health: Some(Box::new(RegularHealth {
current_health: 50.0,
max_health: 50.0,
})),
},
);
let mut player = Player {
id: 0,
name: "FyiaR".to_string(),
companions: Vec::new(),
};
player.companions.push(companion_id);
'GameLoop: loop {
let input = poll_input().trim().to_lowercase();
match input.as_str() {
// TODO: Extract healing component here.
"heal" => {
let companion = companion_storage.get_mut(&companion_id).unwrap();
match companion.health_mut() {
None => {
println!("The selected companion doesn't have health associated with it.");
}
Some(health) => {
heal_command(health);
println!("{} was healed to max.", companion.name);
}
}
}
"q" => {
break 'GameLoop;
}
"s" => {
status_command(&player, &companion_storage); // SAME PROBLEM HERE
}
_ => println!("Unknown command"),
}
}
}
struct Player {
id: u8,
name: String,
companions: Vec<CompanionId>,
}
struct Companion {
id: CompanionId,
name: String,
health: Option<Box<dyn Health>>,
}
struct RegularHealth {
current_health: f32,
max_health: f32,
}
trait Health {
...
}
impl Companion {
fn health_mut(&mut self) -> Option<&mut dyn Health> {
match self.health.as_mut() {
None => None,
Some(health) => Some(health.as_mut()),
}
}
fn health(&self) -> Option<&dyn Health> {
match self.health.as_ref() {
None => None,
Some(health) => Some(health.as_ref()),
}
}
}
impl Health for RegularHealth {
...
}
fn heal_command(health: &mut dyn Health) {
health.heal_max();
}
fn status_command(player: &Player, companion_storage: &HashMap<CompanionId, Companion>) {
println!("Status for {}: ", player.name);
println!("===============================");
print!("Companions: ");
for companion_id in &player.companions {
let companion = companion_storage.get(companion_id).unwrap();
match companion.health.as_ref() {
None => {}
Some(health) => {
print!(
"{} [{}/{}], ",
companion.name,
health.health(),
health.max_health()
);
}
}
}
println!();
println!("===============================");
}

Related

Wrap type in enum and return reference

type Id = u8;
struct A {
id: Id,
}
struct B {
id: Id,
}
struct C {
id: Id,
}
struct State {
a_vec: Vec<A>,
b_vec: Vec<B>,
c_vec: Vec<C>,
}
impl State {
fn new() -> Self {
Self {
a_vec: Vec::new(),
b_vec: Vec::new(),
c_vec: Vec::new(),
}
}
fn get_e0(&self, id: Id) -> &E0 {
if let Some(a) = self.a_vec.iter().find(|x| x.id==id) {
&E0::A(a)
} else if let Some(b) = self.b_vec.iter().find(|x| x.id==id) {
&E0::B(b)
} else {
panic!("ahh that id doesn't exist everbody panic!!!")
}
}
fn get_e0_mut(&mut self, id: Id) -> &mut E0 {
if let Some(a) = self.a_vec.iter_mut().find(|x| x.id==id) {
&mut E0::A(a)
} else if let Some(b) = self.b_vec.iter_mut().find(|x| x.id==id) {
&mut E0::B(b)
} else {
panic!("ahh that id doesn't exist everbody panic!!!")
}
}
}
enum E0 {
A(A),
B(B),
}
enum E1 {
A(A),
C(C),
}
fn main() {
let state = State::new();
let a0 = A { id: 0 };
let a1 = A { id: 1 };
let b0 = B { id: 2 };
let c0 = C { id: 3 };
state.a_vec.push(a0);
state.a_vec.push(a1);
state.b_vec.push(b0);
state.c_vec.push(c0);
let e5 = state.get_e0(1);
}
I'm looking for a way to implement the function get_e0 and get_e0_mut that wrap several types into an enum so the caller doesn't have to care which of A or B their id relates to, only that they will get an E0. Yet an Vec of E0's seems unfeasible as there might be separate grouping such as E1.
If these functions are not possible then is there another method that could be used to reduce the overhead of searching all the respective Vec's individually each time.
It is guaranteed that the all id's are unique.
You cannot return a reference to a temporary. Instead, you can make your enums generic over their contents. You can therefore use a single enum:
enum E0<T, U> {
A(T),
B(U),
}
You can then use it like this:
fn get_e0(&self, id: Id) -> E0<&A, &B> {
if let Some(a) = self.a_vec.iter().find(|x| x.id == id) {
E0::A(a)
} else if let Some(b) = self.b_vec.iter().find(|x| x.id == id) {
E0::B(b)
} else {
panic!("ahh that id doesn't exist everbody panic!!!")
}
}
fn get_e0_mut(&mut self, id: Id) -> E0<&mut A, &mut B> {
if let Some(a) = self.a_vec.iter_mut().find(|x| x.id == id) {
E0::A(a)
} else if let Some(b) = self.b_vec.iter_mut().find(|x| x.id == id) {
E0::B(b)
} else {
panic!("ahh that id doesn't exist everbody panic!!!")
}
}
Thanks to lifetime elision rules, you don't have to specify lifetimes.
Playground link
Note that if you want to avoid the panic, your return type should express the notion that there can be no value found.
You can for example return an Option:
fn get_e0(&self, id: Id) -> Option<E0<&A, &B>> { ... }
Or alter the enum to have a None variant, similar to Option:
enum E0<T, U> {
A(T),
B(U),
None,
}
And use it like this:
fn get_e0(&self, id: Id) -> E0<&A, &B> {
if let Some(a) = self.a_vec.iter().find(|x| x.id==id) {
E0::A(a)
} else if let Some(b) = self.b_vec.iter().find(|x| x.id==id) {
E0::B(b)
} else {
E0::None
}
}
It is most of the time more idiomatic to express such situations using the type system instead of panicking.

How do I use an impl function in the main function in rust?

I want to use a function in the main function in a rust program that I am building to help me learn rust and come up with an error: self value is a keyword only available in methods with a self parameterrustc(E0424). What can I fix in my code so that this error does not happen?
pub use crate::user_account::user_account;
use rand::Rng;
#[allow(dead_code)]
pub trait UserInfo {
fn user_info(&mut self);
fn acc_no(&mut self);
fn yes(self);
fn bank_new_user(self);
}
pub struct NewUser {
age: String,
new_user: String,
account: String,
account_number: i32,
routing_number: i32,
select: String,
}
impl UserInfo for NewUser {
fn user_info(&mut self) {
self.age = String::new();
self.new_user = String::new();
println!("What is your name?");
print!("Name: ");
std::io::stdin().read_line(&mut self.new_user);
println!(" ");
println!("Hello {}, What is your age? ", self.new_user);
std::io::stdin().read_line(&mut self.age);
let age2: String = self.age.trim().into();
}
fn acc_no(&mut self) {
println!(
"We will generate a new account number \
and routing number for you."
);
self.account_number = rand::thread_rng().gen_range(10000000..99999999);
println!("Your account number is {}", self.account_number);
self.routing_number = rand::thread_rng().gen_range(10000000..99999999);
println!("Your account routing number is {}", self.routing_number);
}
fn yes(self) {
NewUser::user_info(&mut self);
NewUser::acc_no(&mut self);
}
//function I want to use in main.
fn bank_new_user(self) {
self.account = String::new();
println!("Would you like to make a new account with us today?");
loop {
println!(
" yes: continue to application, no: continue browsing , \
or exit: to exit"
);
self.account.clear();
std::io::stdin()
.read_line(&mut self.account)
.expect("please type yes, no or exit.");
let account = self.account.trim();
match account {
"yes" => {
self.yes();
break;
}
"no" => {
println!("You do not need an account to continue browsing.");
println!("Have a wonderful day and thank you for considering Mars Banking!");
break;
}
"exit" => {
println!(
"Thank you for choosing Mars Banking for your banking needs!\
Have a wonderful day!"
);
break;
}
_ => {
println!("Error! Enter yes, no, or exit.")
}
}
}
}
}
pub mod new_user;
mod settings;
mod user_account;
pub use crate::settings::settings;
pub use crate::user_account::user_account;
use new_user::NewUser;
use new_user::UserInfo;
fn main() {
loop{
let mut select = String::new();
println!("Welcome to Mars Banking!");
println!("What would you like to do today?");
println!("Create a new account: 1\nLogin: 2\nSettings: 3\nExit: 4");
select.clear();
std::io::stdin().read_line(&mut select);
let select = select.trim();
match select {
//Here is where the error happens.
"1" => NewUser::bank_new_user(self),
"2" => user_account(),
"3" => settings(),
"4" => break,
_ => {}
}
}
}
The conventional pattern for this sort of constructor is a static method that doesn't take a self argument, like this:
impl NewUser {
fn bank_new_user() {
let mut new_user = NewUser { /* initialize the fields */ };
// Edit or use new_user as necessary
}
}
you can see an example of this here, in the methods defined for Point:
struct Point {
x: f64,
y: f64,
}
// Implementation block, all `Point` associated functions & methods go in here
impl Point {
// This is an "associated function" because this function is associated with
// a particular type, that is, Point.
//
// Associated functions don't need to be called with an instance.
// These functions are generally used like constructors.
fn origin() -> Point {
Point { x: 0.0, y: 0.0 }
}
// Another associated function, taking two arguments:
fn new(x: f64, y: f64) -> Point {
Point { x: x, y: y }
}
}
notice how niether origin nor new take self as an argument.

Rust first step, how to create a Option<Box<T>> from &mut self?

I'm learning Rust for a few days and honestly some concepts are really difficult to understand and apply. I started to rewrite a small part of a component in order to compare the legendary speed of Rust and learn by a concrete project. It's a component to measure time and monitor the program during the execution. It will be a dynamic library used by another program.
My question :
1) How to create an Option<Box<T>> from &mut self ? (fn add_son)
extern crate winapi;
extern crate user32;
extern crate kernel32;
struct KpiMethod{
element : String,
line : u32,
nb_occ : u32,
counter_start : i64,
counter_end : i64,
total_time: i64,
kpi_fils : Vec<KpiMethod>,
kpi_father : Option<Box<KpiMethod>>
}
impl KpiMethod {
pub fn new(_element: String, _line: u32, _father: Option<Box<KpiMethod>>) -> KpiMethod {
KpiMethod{
element : _element,
line : _line,
nb_occ : 1,
counter_start : get_counter(),
counter_end : 0,
total_time: 0,
kpi_fils : Vec::new(),
kpi_father : _father
}
}
pub fn add_son(&mut self, _element: String, _line: u32) -> KpiMethod{
//How create a Option<Box<KpiMethod>> of an existing KpiMethod (self) ?
let mut kpimet = KpiMethod::new(_element, _line, Some(Box::new(self)));
//Do I need a mutable self to push ?
self.kpi_fils.push(kpimet);
kpimet
}
pub fn find_son(&mut self, _element: String, _line: u32) -> Option<&KpiMethod> {
//which is the good and speed method to find a son with key (element,line) ?
for elem in self.kpi_fils.iter_mut() {
if elem.element == _element && elem.line == _line {
//why do I put a return here to fix error ?
return Some(elem)
}
}
None
}
}
pub struct KpiAgent{
kpi_Method : Vec<KpiMethod>,
current_Method : Option<Box<KpiMethod>>,
counter_start : i64,
counter_end : i64,
date_start : String,
date_end : String,
auto_consommation : u64,
}
impl KpiAgent {
pub fn new() -> KpiAgent {
KpiAgent{
kpi_Method: Vec::new(),
current_Method: None,
counter_start: 0,
counter_end: 0,
date_start: String::from(""),
date_end: String::from(""),
auto_consommation: 0
}
}
pub fn method_start(&mut self, element: String, line: u32){
match self.current_Method {
None => {
self.current_Method = Some(Box::new(KpiMethod::new(element, line, None)));
if self.counter_start == 0 {
self.counter_start = get_counter();
}
},
Some(method) => {
let metfils = method.find_son(element, line);
match metfils {
None => {
self.current_Method = Some(Box::new(method.add_son(element, line)));
},
Some(son) => {
son.nb_occ += 1;
son.counter_start = get_counter();
}
}
},
}
}
pub fn method_end(&mut self, element: String, line: u32){
match self.current_Method{
Some(met) => {
met.counter_end = get_counter();
self.counter_end = met.counter_end;
met.total_time += met.counter_end - met.counter_start;
self.current_Method = met.kpi_father;
}
}
}
}
pub fn get_counter() -> i64 {
let mut counter: i64 = 0;
unsafe{
kernel32::QueryPerformanceCounter(&mut counter);
}
counter
}
pub fn main() {
let mut met = KpiMethod::new("1c".to_string(), 1, None);
met.add_son("2c".to_string(),2);
met.add_son("3c".to_string(),3);
met.add_son("4c".to_string(),4);
let _toto = met.find_son("3c".to_string(),3);
match _toto{
None => println!("Not found"),
Some(x) => println!("{}",x.element),
}
let mut agent = KpiAgent::new();
agent.method_start("test".to_string(),2);
agent.method_end("test".to_string(),10);
}

How to generalise access to struct fields?

I try to find differences from two streams (represented by iterators) for later analysis, the code below works just fine, but looks a little bit ugly and error prone (copy-paste!) in updating values in update_v? functions. Is there any ways to generalise it assuming that source is matter?
struct Data {};
struct S {
v1: Option<Data>,
v2: Option<Data>
}
...
fn update_v1(diffs: &mut HashMap<u64, Data>, key: u64, data: Data) {
match diffs.entry(key) {
Entry::Vacant(v) => {
let variant = S {
v1: Some(data),
v2: None
};
v.insert(variant);
},
Entry::Occupied(e) => {
let new_variant = Some(data);
if e.get().v2 == new_variant {
e.remove();
} else {
let existing = e.into_mut();
existing.v1 = new_variant;
}
}
}
}
fn update_v2(diffs: &mut HashMap<u64, Data>, key: u64, data: Data) {
match diffs.entry(key) {
Entry::Vacant(v) => {
let variant = S {
v2: Some(data),
v1: None
};
v.insert(variant);
},
Entry::Occupied(e) => {
let new_variant = Some(data);
if e.get().v1 == new_variant {
e.remove();
} else {
let existing = e.into_mut();
existing.v2 = new_variant;
}
}
}
}
Instead of writing one function for each field, receive a pair of Fns as arguments:
fn(&S) -> Option<Data>, which can be used to replace this condition
if e.get().v1 == new_variant { /* ... */ }
with this
if getter(e.get()) == new_variant { /* ... */ }
fn(&mut S, Option<Data>) -> (), which replaces
existing.v2 = new_variant;
with
setter(&mut existing, new_variant);
Then on the call site you pass a couple lambdas like this
Getter: |d| d.v1
Setter: |s, d| s.v2 = d
Or vice-versa for the other function.
And if you want to keep the update_v1 and update_v2 function names, just write those as wrappers to this new generalized function that automatically pass the proper lambdas.
You can create a trait to facilitate different ways of accessing the structure.
trait SAccessor {
type RV;
fn new(Data) -> S;
fn v2(&S) -> &Self::RV;
fn v1_mut(&mut S) -> &mut Self::RV;
}
struct DirectSAccessor;
impl SAccessor for DirectSAccessor {
type RV = Option<Data>;
fn new(data: Data) -> S {
S {
v1: Some(data),
v2: None
}
}
fn v2(s: &S) -> &Self::RV {
&s.v2
}
fn v1_mut(s: &mut S) -> &mut Self::RV {
&mut s.v1
}
}
fn update<A>(diffs: &mut HashMap<u64, S>, key: u64, data: Data)
where A: SAccessor<RV=Option<Data>>
{
match diffs.entry(key) {
Entry::Vacant(v) => {
let variant = A::new(data);
v.insert(variant);
},
Entry::Occupied(e) => {
let new_variant = Some(data);
if A::v2(e.get()) == &new_variant {
e.remove();
} else {
let existing = e.into_mut();
*A::v1_mut(existing) = new_variant;
}
}
}
}
// ...
// update::<DirectSAccessor>( ... );
Full code

Polymorphism in Rust and trait references (trait objects?)

I'm writing a process memory scanner with a console prompt interface in Rust.
I need scanner types such as a winapi scanner or a ring0 driver scanner so I'm trying to implement polymorphism.
I have the following construction at this moment:
pub trait Scanner {
fn attach(&mut self, pid: u32) -> bool;
fn detach(&mut self);
}
pub struct WinapiScanner {
pid: u32,
hprocess: HANDLE,
addresses: Vec<usize>
}
impl WinapiScanner {
pub fn new() -> WinapiScanner {
WinapiScanner {
pid: 0,
hprocess: 0 as HANDLE,
addresses: Vec::<usize>::new()
}
}
}
impl Scanner for WinapiScanner {
fn attach(&mut self, pid: u32) -> bool {
let handle = unsafe { OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid) };
if handle == 0 as HANDLE {
self.pid = pid;
self.hprocess = handle;
true
} else {
false
}
}
fn detach(&mut self) {
unsafe { CloseHandle(self.hprocess) };
self.pid = 0;
self.hprocess = 0 as HANDLE;
self.addresses.clear();
}
}
In future, I'll have some more scanner types besides WinapiScanner, so, if I understand correctly, I should use a trait reference (&Scanner) to implement polymorphism. I'm trying to create Scanner object like this (note the comments):
enum ScannerType {
Winapi
}
pub fn start() {
let mut scanner: Option<&mut Scanner> = None;
let mut scanner_type = ScannerType::Winapi;
loop {
let line = prompt();
let tokens: Vec<&str> = line.split_whitespace().collect();
match tokens[0] {
// commands
"scanner" => {
if tokens.len() != 2 {
println!("\"scanner\" command takes 1 argument")
} else {
match tokens[1] {
"list" => {
println!("Available scanners: winapi");
},
"winapi" => {
scanner_type = ScannerType::Winapi;
println!("Scanner type set to: winapi");
},
x => {
println!("Unknown scanner type: {}", x);
}
}
}
},
"attach" => {
if tokens.len() > 1 {
match tokens[1].parse::<u32>() {
Ok(pid) => {
scanner = match scanner_type {
// ----------------------
// Problem goes here.
// Object, created by WinapiScanner::new() constructor
// doesn't live long enough to borrow it here
ScannerType::Winapi => Some(&mut WinapiScanner::new())
// ----------------------
}
}
Err(_) => {
println!("Wrong pid");
}
}
}
},
x => println!("Unknown command: {}", x)
}
}
}
fn prompt() -> String {
use std::io::Write;
use std::io::BufRead;
let stdout = io::stdout();
let mut lock = stdout.lock();
let _ = lock.write(">> ".as_bytes());
let _ = lock.flush();
let stdin = io::stdin();
let mut lock = stdin.lock();
let mut buf = String::new();
let _ = lock.read_line(&mut buf);
String::from(buf.trim())
}
It's not a full program; I've pasted important parts only.
What am I doing wrong and how do I implement what I want in Rust?
Trait objects must be used behind a pointer. But references are not the only kind of pointers; Box is also a pointer!
let mut scanner: Option<Box<Scanner>> = None;
scanner = match scanner_type {
ScannerType::Winapi => Some(Box::new(WinapiScanner::new()))
}

Resources