Вызов конкретной реализации признака в неродовой структуре

#rust #traits

#Ржавчина #Трейты

Вопрос:

У меня есть неродовая структура, которая реализует общую черту. Когда я вызываю функцию в структуре, я получаю следующую ошибку:

 error[E0282]: unable to infer enough type information about `_`
  --> src/main.rs:35:18
   |
35 |     cpu.debugger.attach();
   |                  ^^^^^^ cannot infer type for `_`
   |
   = note: type annotations or generic parameter binding required
  

Я пытался добавлять аннотации типов и привязки общих параметров, но я, очевидно, делаю что-то не так; Я все еще не могу заставить его скомпилироваться. У меня есть аналогичный код в другом месте с универсальной структурой, которая работает, предположительно, потому, что общие границы, разделяемые struct и trait impl, позволяют компилятору определять фактическую реализацию метода для вызова.

Лучший способ проиллюстрировать проблему — привести сокращенный пример:

 struct Cpu<M: Memory, D: Debugger<M>> {
    mem: M,
    debugger: D,
}

impl<M: Memory, D: Debugger<M>> Cpu<M, D> {
    fn new(mem: M, debugger: D) -> Self {
        Cpu {
            mem: mem,
            debugger: debugger,
        }
    }
}

trait Memory {}

struct SimpleMemory;

impl Memory for SimpleMemory {}

trait Debugger<M: Memory> {
    fn attach(amp;mut self) {}
    fn step(mem: amp;M) {}
}

struct NoOpDebugger;

impl<M: Memory> Debugger<M> for NoOpDebugger {}

fn main() {
    let mut cpu = Cpu::new(SimpleMemory, NoOpDebugger);
    cpu.debugger.attach(); // <-- cannot infer type for `_`
}
  

Пожалуйста, извините за плохое название, но это лучший способ, который я знаю, как описать проблему.

Ответ №1:

У вас есть несколько вариантов.

  1. Вы можете указать, для какого конкретного признака вы хотите вызвать attach метод.

     fn main() {
        let mut cpu = Cpu::new(SimpleMemory, NoOpDebugger);
        Debugger::<SimpleMemory>::attach(amp;mut cpu.debugger);
    }
      

    или

     fn main() {
        let mut cpu = Cpu::new(SimpleMemory, NoOpDebugger);
        <NoOpDebugger as Debugger<SimpleMemory>>::attach(amp;mut cpu.debugger);
    }
      
  2. Вы можете переместить attach метод в супер-признак, который не является универсальным.

     trait DebuggerBase {
        fn attach(amp;mut self) {}
    }
    
    trait Debugger<M: Memory>: DebuggerBase {
        fn step(mem: amp;M) {}
    }
    
    impl DebuggerBase for NoOpDebugger {}
    impl<M: Memory> Debugger<M> for NoOpDebugger {}
      
  3. Вы можете добавить PhantomData элемент NoOpDebugger и сделать NoOpDebugger его универсальным, чтобы каждый NoOpDebugger<M> реализовывал только Debugger<M> для одного и того же M . В вашем примере M for NoOpDebugger будет выведен из вызова to Cpu::new .

     use std::marker::PhantomData;
    
    struct NoOpDebugger<M>(PhantomData<M>);
    
    impl<M: Memory> Debugger<M> for NoOpDebugger<M> {}
    
    fn main() {
        let mut cpu = Cpu::new(SimpleMemory, NoOpDebugger(PhantomData));
        cpu.debugger.attach();
    }
      
  4. Если реализации Debugger не зависят от M , и если вы не используете Debugger в качестве объекта признака, то вы можете переместить параметр типа в методы, которые в нем нуждаются, и опустить его в методах, которые в нем не нуждаются.

     trait Debugger {
        fn attach(amp;mut self) {}
        fn step<M: Memory>(mem: amp;M) {}
    }