The lifetime parameter is not constrained by the impl trait, self type, or predicates

I want to implement std::ops::Index and std::ops::Index::Mut for a graph data structure I have. When getting a node, the graph returns a struct NodeRef or NodeMut which takes a reference to the graph itself. But I can't figure out how (and GAT's don't seem to help either) how to get the lifetime of 'graph to be taken into account:

impl<'graph, M: 'static> std::ops::Index<NodeHandle> for SceneGraph<M> {
    type Output = NodeRef<'graph, M>;

    fn index(&self, handle: NodeHandle) -> &Self::Output {
        &self.get_node(handle).expect("handle to be valid")
    }
}

This gives the error:

the lifetime parameter `'graph` is not constrained by the impl trait, self type, or predicates

Effectively, 'graph is for the duration of the SceneGraph struct that the Index trait is being implemented on. How can I make this happen?

As currently standing, the implementation doesn't make sense because it says that the output type has lifetime 'graph independent of what 'graph the caller chooses. This surely must be wrong – based on your description, the lifetime pretty much does depend on M.

However, neither M nor SceneGraph itself seems to have any lifetime parameters. Do you want the return type to be tied to the (implicit) lifetime of the self reference? Because you would write that using GATs (Output would have to have a lifetime parameter).

That isn't, however, compatible with Index. First of all, because Index doesn't make Output a GAT, second, because you must always return a reference from Index. Assuming your NodeRef<'graph> already holds a reference of lifetime 'graph, you will want to return it by-value, but that's not allowed by the signature of Index. (Your current attempt would also result in an error because you're returning the address of a local temporary.)

TL;DR: just write an inherent method instead:

fn get_node_unwrap(&self, handle: NodeHandler) -> NodeRef<'_, M> {
    self.get_node(handle).expect("valid node handle")
}
2 Likes

I hadn't clocked the point about needing to return a reference, and while NodeRef is a reference into the graph it's not a reference itself and so would be a reference to a temporary.

I'll probably just stick with doing the unwrap in the calling code in this case.