A weird "type mismatch" error occurred when practising Composite Pattern

When I tried to transfer the Jave example given by Head First Design to Rust codes, a weird "type mismatch" error occurred.
This seems easy to solve but, after my double-check, I still cannot figure out why the error occurred and how to solve it. Can someone be so kind as to help me out? THANKS!
Codes related are below:

pub type DynMenuIter = Box<dyn Iterator<Item=Box<dyn MenuComponent>>>;

pub struct CompositeIterator{
    stack: Vec<DynMenuIter>
}

impl CompositeIterator{
    pub fn new(iter: DynMenuIter) -> Self{
        let mut stack = Vec::new();
        stack.push(iter);
        CompositeIterator { stack }
    }
}

pub trait MenuComponent{
   fn get_iter(&mut self) -> Option<DynMenuIter>;
   // some other methods
}

pub struct Menu{
    name: String,
    description: String,
    menu_components: HashSet<Box<dyn MenuComponent>>
}
impl MenuComponent for Menu{
    fn get_iter(&mut self) -> Option<DynMenuIter>{
        // error occurred here when newing a CompositeIterator
        let composite_iter = CompositeIterator::new(Box::new(self.menu_components.iter())); 
        Some(Box::new(composite_iter))
    }
}

And the error is below:

error[E0271]: type mismatch resolving `<std::collections::hash_set::Iter<'_, std::boxed::Box<dyn composite_pattern::backbone::MenuComponent>> as std::iter::Iterator>::Item == std::boxed::Box<dyn composite_pattern::backbone::MenuComponent>`
   --> src\composite_pattern\backbone.rs:113:53
    |
113 |         let composite_iter = CompositeIterator::new(Box::new(self.menu_components.iter()));
    |                                                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected reference, found struct `std::boxed::Box`
    |
    = note: expected type `&std::boxed::Box<dyn composite_pattern::backbone::MenuComponent>`
               found type `std::boxed::Box<dyn composite_pattern::backbone::MenuComponent>`
    = note: required for the cast to the object type `dyn std::iter::Iterator<Item = std::boxed::Box<dyn composite_pattern::backbone::MenuComponent>>`

I am totally comfused by the first note, and I don't know why a reference is expected since I coded pub fn new(iter: DynMenuIter) -> Self for CompositeIterator.
I think these codes above are related to the error, but if you cannot understand what these codes do, please let me know or refer to source codes (https://github.com/ifsheldon/RustyOOPatterns/tree/master/src/composite_pattern) uploaded to Github on my repo.

The problem is with your definition of DynMenuIter and the type of self.menu_components.iter(). The elements of HashSet::iter are references to the elements stored in the set. However, your definition of DynMenuIter requires owned elements.

If you change your code as follows, it compiles:

use std::collections::HashSet;

pub type DynMenuIter<'a> = Box<dyn Iterator<Item=&'a Box<dyn MenuComponent>> + 'a>;

pub struct CompositeIterator<'a> {
    stack: Vec<DynMenuIter<'a>>
}

impl<'a> CompositeIterator<'a>{
    pub fn new(iter: DynMenuIter<'a>) -> Self{
        let mut stack = Vec::new();
        stack.push(iter);
        CompositeIterator { stack }
    }
}

pub trait MenuComponent {
   fn get_iter(&mut self) -> CompositeIterator;
   // some other methods
}

pub struct Menu{
    name: String,
    description: String,
    menu_components: HashSet<Box<dyn MenuComponent>>
}
impl MenuComponent for Menu{
    fn get_iter(&mut self) -> CompositeIterator {
        // error occurred here when newing a CompositeIterator
        CompositeIterator::new(Box::new(self.menu_components.iter()))
    }
}

Note however that your MenuComponent remains borrowed as long as you're using the CompositeIterator.

2 Likes

Thank you! The error is solved, but another one comes up.
I have implemented the Iterator trait for CompositeIterator. After changing some places of next() of Iterator to adjust the changes you gave, another error related to references came up. I tried to fix it but failed. Could you please help me again?
The implementation of next() is below:

impl<'a> Iterator for CompositeIterator<'a>
{
    type Item = &'a Box<dyn MenuComponent>;

    /// recursively iterate
    fn next(&mut self) -> Option<Self::Item> {
        match self.stack.last_mut() {
            None => None ,
            Some(top) =>
                {
                    match top.next() {
                        None => {
                            self.stack.pop();
                            self.next()
                        },
                        Some(mut next_one) => { // error here
                            match next_one.get_iter() {
                                None =>  Some(&next_one),
                                Some(iter) => {
                                    self.stack.push(iter);
                                    Some(&next_one)
                                }
                            }
                        }
                    }
                }
        }
    }
}

The error is:

error[E0596]: cannot borrow `**next_one` as mutable, as it is behind a `&` reference
  --> src\composite_pattern\peripheries.rs:56:35
   |
55 |                         Some(mut next_one) => {
   |                              ------------ help: consider changing this to be a mutable reference: `&mut std::boxed::Box<dyn composite_pattern::backbone::MenuComponent>`
56 |                             match next_one.get_iter() {
   |                                   ^^^^^^^^ `next_one` is a `&` reference, so the data it refers to cannot be borrowed as mutable

error: aborting due to previous error

Your fn get_iter(&mut self) requires a mutable reference, e.g. a &mut dyn MenuComponent. However, you have a &'a Box<dyn MenuComponent> so you can't get that. Is there a reason get_iter takes &mut self instead of &self?

I thought iter() of the HashSet menu_components needs &mut self , so I wrote &mut self . But I was wrong. &self work just fine here.
However, what if we, somehow, need &mut self here? Do we have to use internal mutability?

You'd need to adjust your DynMenuIter to have &mut elements, and HashSet doesn't let you iterate mutably over its elements, so you'd have to use a different container (cf. Vec::iter_mut). If that's not an option you could investigate interior mutability. Yet another option is to completely change your data structure, see https://raphlinus.github.io/personal/2018/05/08/ecs-ui.html.

OK! Got that. THANKS!

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.