I'm trying to make a tree with parent pointers in Rust. A method on the node struct is giving me lifetime issues. Here's a minimal example, with lifetimes written explicitly so that I can understand them:
use core::mem::transmute;
pub struct LogNode<'n>(Option<&'n mut LogNode<'n>>);
impl<'n> LogNode<'n> {
pub fn child<'a>(self: &'a mut LogNode<'n>) -> LogNode<'a> {
LogNode(Some(self))
}
pub fn transmuted_child<'a>(self: &'a mut LogNode<'n>) -> LogNode<'a> {
unsafe {
LogNode(Some(
transmute::<&'a mut LogNode<'n>, &'a mut LogNode<'a>>(self)
))
}
}
}
Rust complains about child...
error[E0495]: cannot infer an appropriate lifetime for lifetime parameter
'ndue to conflicting requirements
...but it's fine with transmuted_child.
I think I understand why child won't compile: the self parameter's type is &'a mut LogNode<'n> but the child node contains an &'a mut LogNode<'a>, and Rust doesn't want to coerce LogNode<'n> to LogNode<'a>. If I change the mutable references to shared references, it compiles fine, so it sounds like the mutable references are a problem specifically because &mut T is invariant over T (whereas &T is covariant). I guess the mutable reference in LogNode bubbles up to make LogNode itself invariant over its lifetime parameter.
But I don't understand why that's true—intuitively it feels like it's perfectly sound to take LogNode<'n> and shorten its contents' lifetimes by turning it into a LogNode<'a>. Since no lifetime is made longer, no value can be accessed past its lifetime, and I can't think of any other unsound behavior that could happen.
transmuted_child avoids the lifetime issue because it sidesteps the borrow checker, but I don't know if the use of unsafe Rust is sound, and even if it is, I'd prefer to use safe Rust if possible. Can I?
I can think of three possible answers to this question:
childcan be implemented entirely in safe Rust, and here's how.childcannot be implemented entirely in safe Rust, buttransmuted_childis sound.childcannot be implemented entirely in safe Rust, andtransmuted_childis unsound.
Edit 1: Fixed a claim that &mut T is invariant over the lifetime of the reference. (Wasn't reading the nomicon right.)
Edit 2: Fixed my first edit summary.