![](/img/trans.png)
[英]Rust Trait warning: method references the `Self` type in its `where` clause
[英]the trait cannot be made into an object because method `merge` references the `Self` type in this parameter - rust
我正在尝试通过撤消板条箱为我的应用程序实现撤消/重做系统。 我想要一个全局可访问的HISTORY
变量,我可以将Box<dyn Action>
特征对象推送到该变量。
use undo::{Action, History};
lazy_static::lazy_static! {
static ref HISTORY: History<Box<dyn Action<Output = (), Error = String, Target = AppState>>> = History::new();
}
这给出了错误:
error[E0038]: the trait `Action` cannot be made into an object
note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
--> C:\Users\Primary User\.cargo\registry\src\github.com-1ecc6299db9ec823\undo-0.46.0\src\lib.rs:135:8
|
135 | fn merge(&mut self, _: &mut Self) -> Merged {
| ^^^^^ the trait cannot be made into an object because method `merge` references the `Self` type in this parameter
有没有办法解决这个问题?
这是问题特征Action
,来自撤消库源代码:
pub trait Action {
/// The target type.
type Target;
/// The output type.
type Output;
/// The error type.
type Error;
/// Applies the action on the target and returns `Ok` if everything went fine,
/// and `Err` if something went wrong.
fn apply(&mut self, target: &mut Self::Target) -> Result<Self>;
/// Restores the state of the target as it was before the action was applied
/// and returns `Ok` if everything went fine, and `Err` if something went wrong.
fn undo(&mut self, target: &mut Self::Target) -> Result<Self>;
/// Reapplies the action on the target and return `Ok` if everything went fine,
/// and `Err` if something went wrong.
///
/// The default implementation uses the [`apply`](trait.Action.html#tymethod.apply) implementation.
fn redo(&mut self, target: &mut Self::Target) -> Result<Self> {
self.apply(target)
}
/// Used for manual merging of actions.
fn merge(&mut self, _: &mut Self) -> Merged {
Merged::No
}
}
您似乎正在尝试创建一个可以包含不同类型Action
的历史记录,但这并不是undo
的用途。 您实现Action
的类型旨在表示历史中所有可能的操作,因此如果有多种类型,您通常会在enum
上实现Action
来表示这一点。 如果您想使用特征 object 的原因只是为了避免在Action
impl 的所有方法中使用match
语句,我建议您使用 crate enum_dispatch
为您生成此样板。 但是,如果您有充分的理由使用特征而不是枚举(即,您需要应用程序中的一组操作可以被其他代码扩展),那么特征 object 方法可以工作,尽管它会更复杂,更慢一点,可读性更差。 我在下面说明了两种可能的解决方案:
enum_dispatch
enum_dispatch
只是自动为由实现该特征的类型组成的枚举实现特征。 为了使用它来创建可以实现Action
的类型,您将创建自己的特征AppAction
,就像undo::Action
一样,除了返回类型是固定的并且merge
的第二个参数采用枚举类型而不是Self
,像这样:
#[enum_dispatch(AppActionEnum)]
pub trait AppAction {
fn apply(&mut self, target: &mut AppState) -> Result<(), String>;
fn undo(&mut self, target: &mut AppState) -> Result<(), String>;
fn redo(&mut self, target: &mut AppState) -> Result<(), String> {
self.apply(target)
}
fn merge(&mut self, _: &mut AppActionEnum) -> Merged {
Merged::No
}
}
#[enum_dispatch]
pub enum AppActionEnum {
MyAction1,
MyAction2,
...
}
impl Action for AppActionEnum {
type Target = AppState;
type Output = ();
type Error = String;
fn apply(&mut self, target: &mut AppState) -> Result<(), String> {
AppAction::apply(self, target)
}
fn undo(&mut self, target: &mut AppState) -> Result<(), String> {
AppAction::undo(self, target)
}
fn redo(&mut self, target: &mut AppState) -> Result<(), String> {
AppAction::redo(self, target)
}
fn merge(&mut self, other: &mut AppActionEnum) -> Merged {
AppAction::merge(self, other)
}
}
使用此解决方案,您可以通过匹配可以与Self
合并的类型的第二个参数并使用_ => Merged::No
来忽略其他所有内容,从而为特定的具体类型实现AppAction::merge
。
as_any
如果你仍然想使用动态调度,就像之前你可以创建你自己的 trait AppAction
,但是这次将参数merge
为一个 trait object,并为该类型的盒子实现undo::Action
。
pub trait AppAction {
fn apply(&mut self, target: &mut AppState) -> Result<(), String>;
fn undo(&mut self, target: &mut AppState) -> Result<(), String>;
fn redo(&mut self, target: &mut AppState) -> Result<(), String> {
self.apply(target)
}
fn merge(&mut self, _: &mut dyn AppAction) -> Merged {
Merged::No
}
}
impl Action for Box<dyn AppAction> {
type Target = AppState;
type Output = ();
type Error = String;
fn apply(&mut self, target: &mut AppState) -> Result<(), String> {
self.deref_mut().apply(target);
}
fn undo(&mut self, target: &mut AppState) -> Result<(), String> {
self.deref_mut().undo(target);
}
fn redo(&mut self, target: &mut AppState) -> Result<(), String> {
self.deref_mut().redo(target);
}
fn merge(&mut self, other: &mut Box<dyn MyAction>) -> Merged {
self.deref_mut().merge(other.deref_mut());
}
}
但是,现在出现的问题是,当您不知道其他操作的具体类型时,如何实际实现merge
方法。 为此,您将不得不使用Any
来尝试向下转换为可以合并操作的每个具体类型。 理想情况下,这就像在merge
中使用&mut dyn MyAction + Any
和在History
中使用Box<dyn MyAction + Any>
一样简单,但是 Rust 目前不支持复合特征对象。 基于使Any
成为您的 trait 的超特征,有一个有点复杂的解决方法,但您最好使用 crate as_any
为您处理这个问题。 一旦你让as_any::AsAny
成为 AppAction 的AppAction
并为dyn AppAction
实现as_any::Downcast
Downcast ,你就可以像这样编写一个合并实现:
fn merge(&mut self, other: &mut dyn MyAction) -> Merged {
if let Some(other) = other.downcast_mut::<MyOtherAction1>() {
// handle actions of type MyOtherAction1
return Merged::Yes // or Merged::Annul if appropriate
}
if let Some(other) = other.downcast_mut::<MyOtherAction2>() {
// handle actions of type MyOtherAction2
return Merged::Yes // or Merged::Annul if appropriate
}
// and so on for all the other types this action can merge with...
Merged::No
}
同样,这个选项是两者中最令人困惑的,所以只有在你真的需要时才使用它。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.