2017-05-01 9 views
3

私は基本的には同等の2つの特性を持っていますが、どちらか一方が他方より低いレベルのインターフェースを提供します。より高いレベルの形質が与えられれば、より低いレベルの形質を容易に実現することができる。どちらの形質の実装を受け入れるライブラリを作成したいと思います。潜在的にカルにここ"シンプル"と "アドバンス"バージョンの錆特性

// "Lower level" version of the trait 
pub trait RawState { 
    type Cost: std::cmp::Ord + std::ops::Add<Output = Self::Cost> + std::marker::Copy; 
    type CulledChildrenIterator: Iterator<Item = (Self, Self::Cost)>; 
    fn cull(&self) -> Option<Self::Cost>; 
    fn children_with_cull(&self) -> Self::CulledChildrenIterator; 
} 
// "Higher level" version of the trait 
pub trait State: RawState { 
    type ChildrenIterator: Iterator<Item = (Self, Self::Cost)>; 
    fn children(&self) -> Self::ChildrenIterator; 
} 

// Example of how RawState could be implemented using State 
fn state_children_with_cull<S: State> (s: S) 
    -> impl Iterator<Item = (S, S::Cost)> 
{ 
    s.children() 
     .filter_map(move |(state, transition_cost)| 
     state.cull().map(move |emission_cost| 
      (state, transition_cost + emission_cost) 
     ) 
    ) 
} 

は、国家の形質はあなたが.children()子どもの一覧を表示する機能を定義するインターフェイスを提供し、そして.cull()機能:

私の特定のケースでは、ツリーをトラバースするための形質であります状態。

特性は.children_with_cull()の代わりに関数を定義するインターフェースを提供します。このインターフェースでは、子を反復して1回の関数呼び出しでそれらを切り捨てます。これにより、RawStateの実装では、それが判明する子を生成することさえありません。

ほとんどのユーザーはState特性のみを実装し、RawStateの実装はState実装に基づいて自動的に生成されるようにしたいと考えています。しかしながら、Stateを実施する場合、形質の一部は依然としてRawStateの一部である。

#[derive(Clone, Eq, PartialEq, Hash, Debug)] 
struct DummyState {} 

impl State for DummyState { 
    type Cost = u32; 
    type ChildrenIterator = DummyIt; 
    fn emission(&self) -> Option<Self::Cost> { 
     Some(0u32) 
    } 
    fn children(&self) -> DummyIt { 
     return DummyIt {}; 
    } 
} 

"コスト"タイプが州ではなくRawStateで定義されているため、エラーが発生します。潜在的な問題を回避するには、すなわち、国家内部のRawStateのすべての関連する部分を再定義する

pub trait State: RawState { 
    type Cost: std::cmp::Ord + std::ops::Add<Output = Self::Cost> + std::marker::Copy; 
    type ChildrenIterator: Iterator<Item = (Self, Self::Cost)>; 
    fn cull(&self) -> Option<Self::Cost>; 
    fn children(&self) -> Self::ChildrenIterator; 
} 

として国家を定義しかし、その後、コンパイラはあいまいな重複定義文句を言うでしょう。たとえば、のDummyStateの実装では、<Self as State>::Costまたは<Self as RawState>::Costを参照しているかどうかを判断できないため、Self::Costはあいまいです。

+0

「デフォルト実装」(https://doc.rust-lang.org/book/traits.html#default-methods)の「children_with_cull」を持つ1つの特性だけでなく、2つの特性が必要ですか? – trentcl

+1

@trentcl: 'RawState'を直接実装するタイプのものは、' children'が直接使われていなくても 'children'を実装する必要があります。 –

答えて

5

(彼らは戻り値の型にSelfを使用しているので)RawStateState両方がobject-safeていないことを考えると、私はあなたがこれらの特性(すなわち、無&RawState)のための形質オブジェクトを作成する予定がないことを前提としています。

形質オブジェクトを扱うときに形質オブジェクトのみが1つの形質(プラスCopySendSyncなどの一切のメソッドを、持っていない標準ライブラリから選ばれた少数のホワイトリストの特性)を指定することができますので、State: RawStateをバインドsupertraitは、ほとんどが重要です。特性オブジェクトが参照するvtableは、その特性で定義されたメソッドへのポインタのみを含みます。しかし、形質がスーパーマント境界を有する場合、それらの形質からの方法もまたvtableに含まれる。したがって、&State(合法的だった場合)は、children_with_cullにアクセスできます。

スーパーウォーター境界が重要である別の状況は、サブトラートが一部のメソッドに対してデフォルトの実装を提供する場合です。デフォルトの実装では、別の特性のメソッドにアクセスするためにバインドされたsupertraitを使用できます。

Stateのメソッドの既定の実装がないため、特性オブジェクトを使用できないため、何も追加しないため、supertraitバインドをState: RawStateと宣言しないでください(実際には問題)。

この方法では、Stateを実装する必要があるメンバーをRawStateからコピーする必要があります。 Stateは、このように、このように定義されます。

pub trait State: Sized { 
    type Cost: std::cmp::Ord + std::ops::Add<Output = Self::Cost> + std::marker::Copy; 
    type ChildrenIterator: Iterator<Item = (Self, Self::Cost)>; 

    fn cull(&self) -> Option<Self::Cost>; 
    fn children(&self) -> Self::ChildrenIterator; 
} 

(。。私たちはChildrenIteratorSelfを使用しているため拘束State: Sizedが必要であることに注意してくださいRawStateもバインドRawState: Sizedが必要)

最後に、我々は毛布を提供することができますimplStateを実装するすべてのタイプに対してRawStateです。このimplでは、Stateを実装するすべてのタイプが自動的にRawStateを実装します。

impl<T> RawState for T 
where 
    T: State 
{ 
    type Cost = <Self as State>::Cost; 
    type CulledChildrenIterator = std::iter::Empty<(Self, Self::Cost)>; // placeholder 

    fn cull(&self) -> Option<Self::Cost> { <Self as State>::cull(self) } 
    fn children_with_cull(&self) -> Self::CulledChildrenIterator { 
     unimplemented!() 
    } 
} 

矛盾する名前を明確にするための構文に注意してください。<Self as State>RawStateStateへと向かうように複製された2人のメンバーに使用されています。

+0

非常にクール!正確に私が探しているもの。私はオブジェクトの安全性を読む必要がありますが、私のライブラリはtraitオブジェクトの代わりにテンプレート関数を使用しているので、私のコードは正常に動作すると思います。 –

関連する問題