I'm a total newbie to Rust, and I am facing with a problem using "Trait Object"...
For now, I have App struct with panels field which is a Vec of WavePanel struct which implements Panel trait.
Since I have more that implement Panel, I wanted panels to become more generic.
So, I tried specifying Panel as a Trait Object for the field, but it does not work....
This is how it currently looks (BEFORE the change):
use crate::panels::wave::WavePanel;
use crate::panels::Panel;
#[derive(Clone, Debug)]
pub struct App {
points: Vec<Point>,
points_prev: Vec<Point>,
panels: Vec<WavePanel>, // <-- `WavePanel` struct works just fine.
}
impl App {
pub fn new(config: &Config) -> Result<Self, String> {
let color: String = config.color.clone();
let color2: String = config.color2.clone();
let mut panels = vec![];
for panel in &config.panels {
let id = panel.id.clone();
match id.as_str() {
"wave" => {
panels.push(WavePanel::new(
id.as_str(),
color.as_str(),
color2.as_str(),
)?);
}
_ => {}
}
}
Ok(App {
points: vec![],
points_prev: vec![],
panels: panels,
})
}
}
Here is the Panel trait:
pub trait Panel<G: Graphics> {
fn g(&self) -> Rc<RefCell<G>>;
fn reset(&mut self) {
if let Ok(mut g) = self.g().try_borrow_mut() {
let (width, height) = g.size();
g.reset(width, height);
};
}
}
This is how WavePanel implements Panel trait:
#[derive(Clone, Debug)]
pub struct WavePanel {
id: String,
g: Rc<RefCell<WaveGraphics>>,
graph_type: Rc<Cell<GraphType>>,
}
impl Panel<WaveGraphics> for WavePanel {
fn g(&self) -> Rc<RefCell<WaveGraphics>> {
self.g.clone()
}
}
And, here is what I tried (AFTER):
use crate::panels::wave::WavePanel;
use crate::panels::Panel;
#[derive(Clone, Debug)]
pub struct App {
points: Vec<Point>,
points_prev: Vec<Point>,
panels: Vec<Box<dyn Panel>>, // <-- This does not work...
}
impl App {
pub fn new(config: &Config) -> Result<Self, String> {
let color: String = config.color.clone();
let color2: String = config.color2.clone();
let mut panels = vec![];
for panel in &config.panels {
let id = panel.id.clone();
match id.as_str() {
"wave" => {
// Pushing it into Box this time.
panels.push(Box::new(
WavePanel::new(
id.as_str(),
color.as_str(),
color2.as_str(),
)?
));
}
_ => {}
}
}
Ok(App {
points: vec![],
points_prev: vec![],
panels: panels,
})
}
}
Here is the error:
error[E0107]: wrong number of type arguments: expected 1, found 0
--> src/app.rs:15:25
|
15 | panels: Vec<Box<dyn Panel>>,
| ^^^^^ expected 1 type argument
error: aborting due to previous error
For more information about this error, try `rustc --explain E0107`.
error: could not compile `perlin-wave`
Shouldn't Box<dyn Panel> be explicit enough?
Tried so many things, and still no luck...
Please, I desperately need your help....
Edit: SOLVED (2021.3.4)
For the incorrect usage of the trait object, kmdreko was right. What I had for the trait was not Panel but Panel<G: Graphics>, and they are totally different.
As kmdreko mentioned, I stopped feeding G to Panel trait, and I replaced G with dyn Graphics (for the field g), and that nailed it!
It resolved the initial error.
However, there arose another problem...
When I actually push the struct WavePanel which derives Panel trait to panels: Vec<Box<dyn Panel>>, I got the error saying: expected trait object "dyn Panel", found struct "WavePanel". This is a problem about implementing a heterogeneous vec, and it is not kmdreko's fault. As far as the original question in concern, kmdreko's answer should be the correct answer.
For this, I found an explanation in here.
So, it was about Rust inferring WavePanel struct locally instead of dyn Panel trailt object. Right when I create Box::new(WavePanel::new(..)), now I explicitly tell that I want the trait object Vec<Box<dyn Panel>>, and it fixed it.
There was another problem....
Since it relates to the topic, I thought it is worth it to leave a note as well...
So, I have the field g in Panel now having dyn Graphics instead of G in Panel. A similar situation happened now to Graphics trait. Just like I had WavePanel struct for Panel trait, I have WaveGraphics struct for Graphics trait. In a way, Rust is now confused with the type for g field defined in Panel... For this issue, I decided to derive Any trait for Graphics trait. As long as I define as_any_mut() to always return Any struct in Graphics trait, when I actually want to use it, for instance, g.draw(), I can always convert the trait object into Any, and do this: g.as_any_mut().downcast_mut::<WaveGraphics>() .
For the final diff (for the all fix) is found here.