Search code examples
genericsrecursionrusttraitsassociated-types

Error when writing a recursive trait method with an associated type as an argument


I've been updating a library to use Rust's new associated types. The library offers a Node trait for constructing a DSP graph. Below is a simplified version of the trait that produces the same error I'm running into in my library.

use std::default::Default;
use std::num::Float;

trait Node {
    type Output: Default + Float;

    fn inputs<N>(&mut self) -> Vec<&mut N>
        where
            N: Node<Output = <Self as Node>::Output>;

    fn output_requested(&mut self, output: &mut <Self as Node>::Output) {
        for input in self.inputs().into_iter() {
            let mut working: <Self as Node>::Output = Default::default();
            input.output_requested(&mut working);
            //    ^~~~~ ERROR
            *output = *output + working;
        }
    }

}

fn main() {}

Here's the error message

<anon>:15:19: 15:49 error: the type of this value must be known in this context
<anon>:15             input.output_requested(&mut working);
                            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Playpen link - http://is.gd/xm0wvS

Considering that self.inputs() returns N where N: Node<Output = <Self as Node>::Output>, I'm under the impression rustc should have enough type information about input to satisfy the call to the output_requested method?

Any help greatly appreciated!


Solution

  • First of all: given an object x implementing Node, x.inputs() takes a generic parameter N and returns Vec<&mut N>.

    Now let’s write out a more explicitly typed version of what’s happening in output_requested.

    (Incidentally, with the fancy new IntoIterator basis of the for loop, the .into_iter() is no longer necessary.)

    fn output_requested(&mut self, output: &mut <Self as Node>::Output) {
        let inputs: Vec<&mut N> = self.inputs();
        for input in inputs {  // input: &mut N
            let mut working: <Self as Node>::Output = Default::default();
            input.output_requested(&mut working);
            *output = *output + working;
        }
    }
    

    Well, then; what can we figure about this type N? Can we resolve it?

    • It came from self.inputs(), introducing the constraint that it implements Node<Output = <Self as Node>::Output>;

    • On the object, you called the method self.output_requested(&mut <Self as Node>::Output), which only confirms the previous point.

    So all we know about this N is that it implements Node with the same Output as our type. But that could be two completely different types, like this:

    impl Node for A {
        type Output = Out;
        …
    }
    
    impl Node for B {
        type Output = Out;
        …
    }
    

    Thus you can see that it is not possible to determine what N is; it can always be Self, but there may be other possibilities as well, and so it cannot be resolved statically and is forbidden.