首页 > 解决方案 > 使用 Struct 的特征对象 - 错误:类型参数的数量错误

问题描述

我是 Rust 的新手,我在使用“Trait Object”时遇到了问题......

现在,我有App带有字段的结构,它是实现特征panels的结构的 Vec 。 由于我有更多的实现,我想变得更通用。 所以,我尝试指定为该字段的特征对象,但它不起作用....WavePanelPanel
Panelpanels
Panel

这是它目前的样子(更改之前):

https://github.com/minagawah/perlin-experiment/blob/6e8064140daedc0767733898621c951619ff4467/src_for_wasm/perlin-wave/src/app.rs#L14

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,
        })
    }
}

这是Panel特征:

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);
        };
    }
}

这是WavePanel实现Panel特征的方式:

#[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()
    }
}

而且,这是我尝试过的(之后):

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,
        })
    }
}

这是错误:

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`

Box<dyn Panel>不够明确吗?
尝试了很多东西,但仍然没有运气......
拜托,我非常需要你的帮助......

编辑:已解决(2021.3.4)

对于 trait 对象的错误使用,kmdreko 是正确的。我所拥有的特质不是Panelbut Panel<G: Graphics>,它们完全不同。
正如 kmdreko 提到的,我停止喂食Gtrait Panel,我Gdyn Graphics(for the field g) 替换,这就成功了!
它解决了最初的错误。

但是,出现了另一个问题......
当我实际将WavePanel派生Paneltrait 的结构推送到 时panels: Vec<Box<dyn Panel>>,我收到错误消息:expected trait object "dyn Panel", found struct "WavePanel". 这是实现异构vec的问题,不是kmdreko的错。就所关注的原始问题而言,kmdreko 的答案应该是正确的答案。
为此,我在这里找到了解释。
所以,这是关于 RustWavePanel在本地推断结构而不是dyn Paneltrailt 对象。就在我创建的时候Box::new(WavePanel::new(..)),现在我明确地告诉我我想要特征对象Vec<Box<dyn Panel>>,并且它修复了它。

还有另一个问题....
由于它与主题有关,我认为也值得留下一个便条...
所以,我现在拥有的领域g是 in而不是in 。trait现在也发生了类似的情况。就像我有struct for trait 一样,我有struct for trait。在某种程度上,Rust 现在与...中定义的字段类型混淆了。对于这个问题,我决定为 trait 派生 trait 。只要我定义总是在 trait 中返回struct ,当我真正想使用它时,例如,我总是可以将 trait 对象转换为,并执行以下操作:Paneldyn GraphicsGPanelGraphicsWavePanelPanelWaveGraphicsGraphicsgPanelAnyGraphicsas_any_mut()AnyGraphicsg.draw()Anyg.as_any_mut().downcast_mut::<WaveGraphics>() .

对于最终差异(对于所有修复),可在此处找到。

标签: rusttraits

解决方案


不够Box<dyn Panel>明确吗?

不,您已定义Panel为通用的;Panel<A>并且Panel<B>是不同的特征。如果你想Panel使用不管图形类型,它不能是通用的。

如果g()只有支持reset(),您可以将其删除:

pub trait Panel {
    fn reset(&mut self);
}

impl Panel for WavePanel {
    fn reset(&mut self) {
        if let Ok(mut g) = self.g.try_borrow_mut() {
            let (width, height) = g.size();
            g.reset(width, height);
        };
    }
}

或者,如果g()仍然需要一般地获取图形对象,也可以使其成为动态对象:

pub trait Panel {
    fn g(&self) -> Rc<RefCell<dyn Graphics>>;
                           // ^^^^^^^^^^^^

    fn reset(&mut self) {
        if let Ok(mut g) = self.g().try_borrow_mut() {
            let (width, height) = g.size();
            g.reset(width, height);
        };
    }
}

impl Panel for WavePanel {
    fn g(&self) -> Rc<RefCell<dyn Graphics>> {
        self.g.clone()
    }
}

推荐阅读