2015-10-23 92 views
0

试图让动态调度工作在特征静态方法中,但获取类型必须是已知错误。特征动态调度中的静态方法

我想实现像

是让特质通用的唯一途径?

pub struct Aggregate<T: AggregateRoot> 
{ 
    pub id: Uuid, 
    agg: T, 
    changes: Vec<Box<Any>> 
} 

impl <T :AggregateRoot > Aggregate<T> 
{ 
    fn GetUncomittedChanges(&self) -> Vec<Box<Any>> { self.changes} 
    fn MarkChangesAsCommitted(&self) { self.changes.drain(..);} 
} 


trait AggregateRoot 
{ 
    fn new2() -> Self; //should be private 
    fn new(id: Uuid) -> Self; 

    fn LoadsFromHistory(changes : Vec<Box<Any>>) -> Self 
     where Self: Sized 
    { 
     let newAgg = AggregateRoot::new2(); 
     changes.iter().map(|e| newAgg.Apply(e)); 
     newAgg.MarkChangesAsCommitted(); 
     newAgg 
    } 

    fn Apply<U: Any>(&self, arg: U) ; 
    fn GetId(&self) -> Uuid; 
} 

当前尝试,但给出2预计1提供的参数。

+0

的代码提交有问题,并按照上述方法没有错误。相反,在进行代码编译之后,我得到的错误是“特征'core :: marker :: Sized'没有针对”Self“类型和”没有名为'map'的类型“实现。请创建一个[MCVE](/ help/mcve),最好在[Rust Playground]上编译(https://play.rust-lang.org/)。 – Shepmaster

+0

Where子句让你发现错误,但看起来不正确,第二个错误是用iter()修复的。第一个错误是你如何做到这一点..我不知道该怎么做才能编译。这是我目前的尝试 – user1496062

回答

2

让我们从你问这个问题的方式开始,希望你将来能够提出更好的问题。您得到的完整错误是:

<anon>:27:37: 27:52 error: the type of this value must be known in this context 
<anon>:27    changes.iter().map(|e| newAgg.Apply(e)); 
               ^~~~~~~~~~~~~~~ 

请注意,编译器错误消息显示您确切的哪一位代码出错。提问时包含该错误很有用。

你还包括了无关的细节。例如,GetUncomittedChanges,idGetId在您的示例中都未使用。解决问题时,应该生成一个MCVE。这有助于您更好地理解问题,并帮助人们帮助您查看更少的代码,从而加快周转速度。


你的代码中有许多问题,但让我们开始在第一个错误:

let newAgg = AggregateRoot::new2(); 

这是说“对任何可能的AggregateRoot,创建一个新的”。许多具体类型可以实现特征(这是特征点),但编译器需要知道给定实例需要分配多少空间。可能有一个结构需要1个字节或200个字节; 这个大小写需要多少空间分配?

要取得进展,您可以改用Self::new2。这意味着要创建当前实现者的新实例

下一个错误是

<anon>:20:16: 20:40 error: no method named `MarkChangesAsCommitted` found for type `Self` in the current scope 
<anon>:20   newAgg.MarkChangesAsCommitted(); 
         ^~~~~~~~~~~~~~~~~~~~~~~~ 

您正在呼吁从性状实施一个具体类型的方法;这根本没有任何意义。如果bool实现了这个特性会发生什么?它没有MarkChangesAsCommitted方法。在这种情况下,我不知道你想要什么,所以我只是删除它。

现在你得到这个错误:

<anon>:19:9: 19:16 error: `changes` does not live long enough 
<anon>:19   changes.iter().map(|e| newAgg.Apply(e)); 
        ^~~~~~~ 

note: reference must be valid for the static lifetime... 
<anon>:17:5: 21:6 note: ...but borrowed value is only valid for the scope of parameters for function at 17:4 

那是因为你的方法Apply希望给予实现Any一个类型。但是,您通过了&Box<Any>Any的使用期限为'static,并且该引用不是静态的。一个直接的变化是接受对实现Any类型的引用:

fn Apply<U: Any>(&self, arg: &U); 

既然代码编译,还有一些风格问题进行修复:

  1. 没有空间之前:
  2. 没有空间后>
  3. 之前没有空间(
  4. ()
  5. map没有空间不应该被用于副作用
  6. 函数和变量名camel_case
  7. 大部分时间里,接受的&[T]而不是Vec<T>作为函数参数。
  8. 使用“埃及”括号,除非您使用where条款。

总之,代码如下:

use std::any::Any; 

struct Aggregate<T: AggregateRoot> { 
    agg: T, 
    changes: Vec<Box<Any>> 
} 

impl<T: AggregateRoot> Aggregate<T> { 
    fn mark_changes_as_committed(&self) { } 
} 

trait AggregateRoot { 
    fn new() -> Self; 

    fn load_from_history(changes: &[Box<Any>]) -> Self 
     where Self: Sized 
    { 
     let new_agg = Self::new(); 
     for change in changes { new_agg.apply(change) } 
     new_agg 
    } 

    fn apply<U: Any>(&self, arg: &U); 
} 

fn main() {} 

Is there a way to constrain the concrete types of the AggregateRoot to Aggregates so mark_changes can be called?

不,我知道的。这听起来像你想移动到mark_changes性状,并迫使性状的所有实现实现它:

trait AggregateRoot { 
    fn load_from_history(changes: &[Box<Any>]) -> Self 
     where Self: Sized 
    { 
     let new_agg = Self::new(); 
     for change in changes { new_agg.apply(change) } 
     new_agg.mark_changes_as_committed(); 
     new_agg 
    } 

    fn mark_changes_as_committed(&self); 

    // ... 
} 
+0

有没有办法将Aggregateroot的具体类型限制为Aggregates,因此可以调用标记更改?申请是我正在寻找的。当学习一门新语言时,你会尝试很多东西。这说“对于任何可能的AggregateRoot,创建​​一个新的”。这样做的目的是让外部对象可以创建具体的实现并通过事件(通常是一个存储库)来填充它,具体的impl将处理这些事件。调用者可以将其标记为完整,但如果不完成则会导致错误的错误。 – user1496062

+0

@ user1496062我在内部回答了您的问题。我不清楚你的评论中是否隐藏了更多问题:-) – Shepmaster

+0

谢谢。 markchanges特别与数据有关,它清除了底层的集合。你可以从C#/ F#示例中看到我想要做的事情。因为他们有一些我还没有做到的具体实现。请注意,所有impl都将使用结构Aggregate并对其进行扩展,它们之间的关系非常密切。 – user1496062