pub trait CardData {
type UpgradeType: UpgradeData + UpgradeDataExt;
...
}
pub trait UpgradeData {
fn upgrade(&mut self);
}
pub trait UpgradeDataExt {
fn new(is_pre_upgraded: bool) -> Box<Self>;
}
pub struct Card<T>
where
T: Box<UpgradeData + ?Sized>,
{
data: Box<dyn CardData<UpgradeType = T>>,
name: String,
upgrade_info: Box<T>, // upgrade_count: i32,
base_upgrade_info: Box<T>, // base_upgrade_count: i32,
}
impl<T: UpgradeData + UpgradeDataExt> Card<T> {
pub fn new(
card_data: Box<dyn CardData<UpgradeType = T>>,
is_pre_upgraded: bool,
) -> Card<dyn UpgradeData> {
let name = card_data.name();
let c = Self {
data: card_data,
name: name,
upgrade_info: T::new(is_pre_upgraded),
base_upgrade_info: T::new(is_pre_upgraded),
} as _;
// Error is here, c cannot be coerced properly
c
}
...
}
pub struct Defend;
impl CardData for Defend {
type UpgradeType = SingleUpgrade;
...
}
pub struct SearingBlow;
impl CardData for SearingBlow {
type UpgradeType = InfiniteUpgrade;
...
}
//this is the function I wanna write that returns a vector of cards
pub fn starter_deck() -> Vec<Card<dyn UpgradeData>> {
let mut deck = vec![];
deck.push(Card::new(Box::new(SearingBlow::default()), false));
deck.push(Card::new(Box::new(Defend::default()), false));
deck
}```
#How can I Have a function return a struct with a trait object Generic argument
35 messages · Page 1 of 1 (latest)
I'm confused by what do you want to store in update_info and base_update_info. Why do you store Box<T> there, or, even if it were to be T, why do you want to store the whole T there
cards can be upgraded but different cards are upgraded in different ways, some can only be upgraded once, and others infinite times, UpgradeData is a struct that stores data about how many times it's upgraded,how its upgraded, how many times it can be upgraded etc
I wanted flexibility so i thought to make it a struct
I feel like you want is Vec<Box<dyn SomeCardTrait>> where the trait contains some trait with behavior that any card is expected to have.
yeah that's what I had originally
but I wanted to define fields
which you can't do in traits, so I thought to have a struct which has a field of dyn SomeCardTrait and the fields I want
and then have a Vec of those
You can have though get_field(&self) -> &Field and get_mut_field(&mut self) -> &mut Field
yeah but that would involve defining those functions for every implementation of that trait
and I wanted to avoid the boilerplate since it'd be almost the same for each
So you want to wrap the trait into a struct that contains some additional data that will always be of the same type for all cards?
yeah
except the data can sometimes be different because cards upgrade differently, so i tried to wrap that into it's own trait
I don't think you'll be able to do it, where the data is different
At least not with trait objects / type erasure. A trait object is essentially (ptr_to_data, ptr_to_vtable) and that's everything it knows. So there's some one data that can perform a bunch of functions and it doesn't know what that data is. But here you somehow want to split it into two
Although, hmm
like the inelegant solution I have in mind is just have an enum for each type of upgrade, but that's gonna lead to a lot of annoying boilerplate
I guess you could do:
pub struct Card
where
{
data: Box<dyn CardData>>,
name: String,
upgrade_info: Box<dyn UpgradeData>, // upgrade_count: i32,
base_upgrade_info: Box<dyn WhateverElse>, // base_upgrade_count: i32,
}
But it seems a bit strange
and it will not link CardData to UpgradeData in any way. It will store the two datas in a separate place.
that's what I currently have. isn't it?
oh also upgrade_info and base_upgrade_info are the same type
No, you had:
pub struct Card<T>
where
T: Box<UpgradeData + ?Sized>,
{
data: Box<dyn CardData<UpgradeType = T>>,
name: String,
upgrade_info: Box<T>, // upgrade_count: i32,
base_upgrade_info: Box<T>, // base_upgrade_count: i32,
}
in which you seem to attempt to link the type of UpgradeData to CardData, but I don't think you'll be able to do it, if you want to then type erase and store all those structs with trait objects in a vec.
If a Card can store different UpgradeData and you have Vec<Card> then T will be different for each Card. But Vec<Card<T>> will say that T is the same for each card
So you want to erase that T completely also. Which:
upgrade_info: Box<dyn UpgradeData>, // upgrade_count: i32,
base_upgrade_info: Box<dyn UpgradeData>,
will do
yeah but the link is pretty important, this is the full trait
pub trait CardData {
type UpgradeType: UpgradeData + UpgradeDataExt;
fn description(&self, upgrade_info: &Self::UpgradeType) -> String;
fn cost(&self, upgrade_info: &Self::UpgradeType) -> Cost;
fn combatant_effects(&self, upgrade_info: &Self::UpgradeType)
-> Vec<(Target, CombatantEffect)>;
fn card_effects(&self, _upgrade_info: &Self::UpgradeType) -> Vec<CardEffect> {
vec![]
}
...
}
I think I might just throw in the towel and have a vec of enums
I use the upgrade type to signify what the argument needs to be
I think it will need to be an enum
or upgrade_info: Box<dyn UpgradeData>
and I think you'll want to loose UpgradeDataExt constraints everywhere and just implement UpgradeData for Box<dyn UpgradeData> and assuming UpgradeDataExt is a blanket trait for any T: UpgradeData you should be able to use it.
but having associated functions makes them not be object safe or something
It will not be an associated function. It will just have an UpgradeDataExt implementation for Box<dyn UpgradeData> because Box<dyn UpgradeData> itself will implement UpgradeData by just recalling all methods.