Trait problem: Borrowed data escapes outside of associated function

Viewed 246

I am trying to implement a binary tree. I want the node data to be separate, because there are many different ways in which this can be implemented, while algorithms on the tree should be generic and independent of how the data is stored.

But I am running into a weird issue with the borrow checker. Basically, when I switch impl<TValue> Display for dyn Tree<TValue> with impl<TValue> Display for TreeNode<TValue>, the problem goes away. But I have no idea why. Why is the Trait causing this issue?

My code is this:

use std::fmt::{Display, Formatter};

struct TreeNode<TValue> {
    value: TValue,
    left: Option<Box<TreeNode<TValue>>>,
    right: Option<Box<TreeNode<TValue>>>,
}

trait Tree<TValue> {
    fn value(&self) -> &TValue;
    fn left(&self) -> Option<&dyn Tree<TValue>>;
    fn right(&self) -> Option<&dyn Tree<TValue>>;
}

impl<TValue> Display for dyn Tree<TValue>
where
    TValue: Display,
{
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        f.write_str("(")?;
        Display::fmt(self.value(), f)?;
        f.write_str(", ")?;

        match self.left() {
            Some(ref x) => x.fmt(f)?,
            None => f.write_str("None")?,
        }

        f.write_str(", ")?;

        match self.right().as_ref() {
            Some(x) => x.fmt(f)?,
            None => f.write_str("None")?,
        }

        f.write_str(")")
    }
}

impl<TValue> Tree<TValue> for TreeNode<TValue>
where
    TValue: Display,
{
    fn value(&self) -> &TValue {
        &self.value
    }

    fn left(&self) -> Option<&dyn Tree<TValue>> {
        self.left.as_ref().map(|x| &**x as &dyn Tree<TValue>)
    }

    fn right(&self) -> Option<&dyn Tree<TValue>> {
        self.right.as_ref().map(|x| &**x as &dyn Tree<TValue>)
    }
}

fn main() {
    let tree = Box::new(TreeNode {
        value: 1,
        left: Some(Box::new(TreeNode {
            value: 2,
            left: None,
            right: None,
        })),
        right: Some(Box::new(TreeNode {
            value: 3,
            left: None,
            right: None,
        })),
    }) as Box<dyn Tree<i32>>;

    println!("{}", tree);
}

And the compiler prints:

error[E0521]: borrowed data escapes outside of associated function
  --> src\main.rs:24:15
   |
19 |     fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
   |            -----
   |            |
   |            `self` declared here, outside of the associated function body
   |            `self` is a reference that is only valid in the associated function body
   |            let's call the lifetime of this reference `'1`
...
24 |         match self.left() {
   |               ^^^^^^^^^^^
   |               |
   |               `self` escapes the associated function body here
   |               argument requires that `'1` must outlive `'static`

To me, this makes no sense. Nothing in the function body captures this value and tries to keep it beyond the scope of this function. Is this some borrow checker limitation?

1 Answers

There are some mental hoops that the compiler has to jump through to reason about your code. Spoilers, the reason is that when you have dyn Tree<TValue> (where you are implementing Display), the lifetime constraining the type defaults to 'static. So when you call x.fmt(f), the x type must be 'static in order to implement the fmt method, and looking backwards means that the self used in self.left() to get x must be 'static as well. But it's not, and thus the error.

The simple fix is to implement Display for dyn Tree<TValue> with any lifetime:

impl<'a, TValue> Display for dyn Tree<TValue> + 'a
Related