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 (RustyOOPatterns/src/composite_pattern at master · ifsheldon/RustyOOPatterns · GitHub) 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.
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:
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 Entity-Component-System architecture for UI in Rust | Raph Levien’s blog.