简体   繁体   English

Rust:使用泛型特征作为特征参数

[英]Rust: Using a generic trait as a trait parameter

How can I use related generic types in Rust?如何在 Rust 中使用相关的泛型类型?

Here's what I've got (only the first line is giving me trouble):这是我得到的(只有第一行给我带来了麻烦):

impl<G, GS> TreeNode<GS> where G: Game, GS: GameState<G>{
    pub fn expand(&mut self, game: &G){
        if !self.expanded{
            let child_states = self.data.generate_children(game);
            for state in child_states{
                self.add_child_with_value(state);
            }
        }
    }
}

GameState is a trait that is generic to a Game , and self.data implements GameState<Game> of this type. GameStateGame通用的 trait, self.data实现了这种类型的GameState<Game> The compiler tells me编译器告诉我

error[E0207]: the type parameter `G` is not constrained by the impl trait, self type, or predicates
  --> src/mcts.rs:42:6
   |
42 | impl<G, GS> TreeNode<GS> where G: Game, GS: GameState<G>{
   |      ^ unconstrained type parameter

error: aborting due to previous error

but it seems to me like I'm constraining G both in the expand function, and in the fact that G needs to belong to GS .但在我看来,我在expand函数中限制了G ,事实上G需要属于GS I would really appreciate any help.我真的很感激任何帮助。

Edit: Here are some more definitions as of now编辑:截至目前,这里有更多定义

trait GameState<G: Game>: std::marker::Sized + Debug{
    fn generate_children(&self, game: &G) -> Vec<Self>;
    fn get_initial_state(game: &G) -> Self;
}

trait Game{}

struct TreeNode<S> where S: Sized{
    parent: *mut TreeNode<S>,
    expanded: bool,
    pub children: Vec<TreeNode<S>>,
    pub data: S,
    pub n: u32
}

impl<S> TreeNode<S>{
    pub fn new(data: S) -> Self{
        TreeNode {
            parent: null_mut(),
            expanded: false,
            children: vec![],
            data,
            n: 0
        }
    }

    pub fn add_child(&mut self, mut node: TreeNode<S>){
        node.parent = self;
        self.children.push(node);
    }

    pub fn add_child_with_value(&mut self, val: S){
        let new_node = TreeNode::new(val);
        self.add_child(new_node);
    }

    pub fn parent(&self) -> &Self{
        unsafe{
            &*self.parent
        }
    }

}


impl<G, GS> TreeNode<GS> where G: Game, GS: GameState<G>{
    // ...
}

The problem is that G is not constrained, so there may be multiple (possibly conflicting) implementations in this block, since GS maybe implement GameState<G> for multiple G .问题是G不受约束,因此该块中可能有多个(可能是冲突的)实现,因为GS可能为多个G实现GameState<G> The parameter G is ambiguous.参数G不明确。


If you want to keep GameState<G> able to be implemented for multiple G , you should move the constraints from the impl block to the method instead:如果您想让GameState<G>能够为多个G实现,您应该将约束从impl块移动到方法:

// note: G is now a type parameter of the method, not the impl block, which is fine
impl<GS> TreeNode<GS> {
    pub fn expand<G>(&mut self, game: &G) where G: Game, GS: GameState<G> {
        if !self.expanded{
            let child_states = self.data.generate_children(game);
            for state in child_states{
                self.add_child_with_value(state);
            }
        }
    }
}

If you only want GameState to be implemented for a single G , you should make G an associated type of GameState instead of a generic type parameter:如果您只想为单个G实现GameState ,则应使G成为GameState的关联类型而不是泛型类型参数:

trait GameState: std::marker::Sized + Debug {
    type G: Game;
    fn generate_children(&self, game: &Self::G) -> Vec<Self>;
    fn get_initial_state(game: &Self::G) -> Self;
}

// note: now G is given by the GameState implementation instead of
//       being a free type parameter
impl<GS> TreeNode<GS> where GS: GameState {
    pub fn expand(&mut self, game: &GS::G){
        if !self.expanded{
            let child_states = self.data.generate_children(game);
            for state in child_states{
                self.add_child_with_value(state);
            }
        }
    }
}

The concrete type of G cannot be detemined based on the type of TreeNode<GS> ;根据TreeNode<GS>的类型无法确定G的具体类型; it is only known when expand is called.只有在调用expand时才知道。 Note that expand could be called twice with different types for G .请注意,可以使用G不同类型调用expand两次。

You can express this by constraining the type parameters for the method instead of the entire implementation block:您可以通过约束方法的类型参数而不是整个实现块来表达这一点:

impl<GS> TreeNode<GS> {
    pub fn expand<G>(&mut self, game: &G)
    where
        G: Game,
        GS: GameState<G>,
    {
        if !self.expanded {
            let child_states = self.data.generate_children(game);
            for state in child_states {
                self.add_child_with_value(state);
            }
        }
    }
}

If it should not be possible for expand to be called with different G s then this is a problem of your modeling.如果不能用不同的G调用expand ,那么这是您的建模问题。 Another way to fix this is to ensure that the type of G is known for all TreeNode seg:解决此问题的另一种方法是确保所有TreeNode段都知道G的类型:

struct TreeNode<G, S>
where
    S: Sized,
{
    parent: *mut TreeNode<G, S>,
    expanded: bool,
    pub children: Vec<TreeNode<G, S>>,
    pub data: S,
    pub n: u32,
}

And then your original implementation block should work as written, once you account for the extra type parameter.然后,一旦您考虑了额外的类型参数,您的原始实现块应该按编写的方式工作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM