Rust: Использование универсальной черты c в качестве параметра черты - PullRequest
1 голос
/ 20 марта 2020

Как я могу использовать родственные типы c в Rust?

Вот что у меня есть (только первая строка доставляет мне проблемы):

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 - это черта, обобщенная c до Game, и self.data реализует GameState<Game> этого типа. Компилятор сообщает мне

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

, но мне кажется, что я ограничиваю G как в функции expand, так и в том факте, что G должен принадлежать GS. Я был бы очень признателен за любую помощь.

Редактировать: Вот еще несколько определений на данный момент

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

}


Ответы [ 2 ]

5 голосов
/ 20 марта 2020
impl<G, GS> TreeNode<GS> where G: Game, GS: GameState<G>{
    // ...
}

Проблема в том, что G не ограничен, поэтому в этом блоке может быть несколько (возможно, конфликтующих) реализаций, поскольку GS может реализовывать GameState<G> для нескольких G. Параметр G является неоднозначным.


Если вы хотите, чтобы 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);
            }
        }
    }
}

Если вы хотите, чтобы GameState был реализован только для одного G, вы должны сделать G ассоциированным тип GameState вместо универсального c параметра типа:

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);
            }
        }
    }
}
3 голосов
/ 20 марта 2020

Конкретный тип G не может быть определен на основе типа TreeNode<GS>; известно только, когда вызывается expand. Обратите внимание, что expand может быть вызван дважды с различными типами для G.

. Вы можете express это ограничить параметры типа для метода вместо всего блока реализации:

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

Если для expand невозможно вызвать с разными G s, то это проблема вашего моделирования. Еще один способ исправить это - убедиться, что тип G известен всем TreeNode seg:

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

И тогда ваш исходный блок реализации должен работать как написано, как только вы учтете дополнительные введите параметр.

...