Cannot figure out why i cannot access a struct's field

Hi,

i am getting error like this:

error[E0609]: no field `category` on type `&Self`
  --> test.rs:11:14
   |
11 |         self.category
   |              ^^^^^^^^

I cannot figure out why that is. Even the IDE can see that there is a category field available. What am i missing?

struct Order {
    order_item: OrderItem
}

struct OrderItem {
    product_type: ProductType
}

trait ProductType {
    fn get_category(&self) -> Box<Category> {
        self.category
    }
}

struct Food {
    category: Box<Category>    
}
impl ProductType for Food {}

struct HomeAppliance {
    category: Box<Category>
}
impl ProductType for HomeAppliance {}

trait Category {
    fn get_has_bundling(&self) -> bool {
        self.has_bundling
    }
}

struct Perishable{ 
    has_bundling: bool
}
impl Category for Perishable{}

struct NonPerishable{ 
    has_bundling: bool
}
impl Category for NonPerishable{}

fn main() {

}

New user trying out Rust. Apologies if the question is very amateurish.

Hi, when you implement trait methods directly inside the trait definition, you are making a default implementation. The issue is that you could write:

impl ProductType for i32 {}

But i32 has no category field.
What you want is:

trait ProductType {
    fn get_category(&self) -> &dyn Category;
}

impl ProductType for Food {
    fn get_category(&self) -> &dyn Category {
        &*self.category
    }
}

I see. I was trying to avoid having to write that boiler plate code in each of the concrete types of ProductType. If i have 10 concrete types of ProductType i will need to repeat in each of them

fn get_category(&self) -> &dyn Category {
    &self.category
}

That’s where macros shine, it’s a little advanced however. I made a playground using a declarative macro because I haven’t used procedural ones yet. With a procedural macro you could simply do:

#[derive(ProductType)]
struct Food {
    // snip
}

There is an example in the book but it will probably make you jump several chapters, for now I think you should stick with copy/paste.

It seems like you’re trying to mimic common supertype inheritance model, but it’s painful as Rust enforces composition over inheritance idiom. Then why don’t you solve it using composition?

struct Product<T: Kind> {
  category: Box<dyn Category>,
  kind: T,
}

trait Kind { .. }

struct Food { .. }

impl Kind for Food { .. }

If you don’t mind can you help to expand on your suggestion. I don’t get how it will work

I tried copy and paste but hit another mysterious problem

trait ProductType {
    fn get_category(&self) -> &dyn Category;
}

struct Food {
    category: Box<dyn Category>    
}
impl ProductType for Food {
    fn get_category(&self) -> &dyn Category {
        &self.category
    }
}

struct HomeAppliance {
    category: Box<dyn Category>
}
impl ProductType for HomeAppliance {
    fn get_category(&self) -> &dyn Category {
        &self.category
    }
}

Any ideas?

error[E0277]: the trait bound `std::boxed::Box<(dyn Category + 'static)>: Category` is not satisfied
  --> test.rs:27:9
   |
27 |         &self.category
   |         ^^^^^^^^^^^^^^ the trait `Category` is not implemented for `std::boxed::Box<(dyn Category + 'static)>`
   |
   = note: required for the cast to the object type `dyn Category`

It’s my fault, I didn’t dereference when I first posted:

&*self.category

This should work.