#How can I Have a function return a struct with a trait object Generic argument

35 messages · Page 1 of 1 (latest)

summer cradle
#
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
}```
#

@lilac scroll

lilac scroll
#

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

summer cradle
#

I wanted flexibility so i thought to make it a struct

lilac scroll
#

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.

summer cradle
#

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

lilac scroll
#

You can have though get_field(&self) -> &Field and get_mut_field(&mut self) -> &mut Field

summer cradle
#

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

lilac scroll
#

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?

summer cradle
#

yeah

#

except the data can sometimes be different because cards upgrade differently, so i tried to wrap that into it's own trait

lilac scroll
#

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

summer cradle
#

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

lilac scroll
#

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.

summer cradle
#

that's what I currently have. isn't it?

summer cradle
lilac scroll
#

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

summer cradle
#

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

lilac scroll
#

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.

summer cradle
lilac scroll
#

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.