0

In this example, why can I dereference t in the match expression but not on the line directly above?

fn tree_weight_v2(t: &BinaryTree) -> i32 {
    // let x = *t; // if uncommented, error: "Cannot move out of borrowed content"
    match *t {
        BinaryTree::Leaf(payload) => payload,
        BinaryTree::Node(ref left, payload, ref right) => {
            tree_weight_v2(left) + payload + tree_weight_v2(right)
        }
    }
}

#[test]
fn tree_demo_2() {
    let tree = sample_tree();
    assert_eq!(tree_weight_v2(&tree), (1 + 2 + 3) + 4 + 5);
    assert_eq!(tree_weight_v2(&tree), (1 + 2 + 3) + 4 + 5);
    // no error ^ meaning tree_weight_v2 is not taking ownership of tree
}

enum BinaryTree {
    Leaf(i32),
    Node(Box<BinaryTree>, i32, Box<BinaryTree>)
}

fn sample_tree() -> BinaryTree {
    let l1 = Box::new(BinaryTree::Leaf(1));
    let l3 = Box::new(BinaryTree::Leaf(3));
    let n2 = Box::new(BinaryTree::Node(l1, 2, l3));
    let l5 = Box::new(BinaryTree::Leaf(5));

    BinaryTree::Node(n2, 4, l5)
}

playground

I don't believe what the code is doing beyond the match statement to be important - unless of course, that is the source of my confusion.

I'm also curious about how match expressions handle dereferenced values. Specifically, since what the match expression sees is a value of type BinaryTree (without any references), why does the match expression not try to take ownership over it? More generally, how does Rust's match interpret the difference between a dereferenced pointer to a value and a normal value?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
sce415
  • 11
  • 1
  • 2
    You're trying to move a borrowed value which isn't allowed by the borrowchecker. `match` just borrows the value (which is valid; you can borrow from a borrow). That would be equivalent to `let x = &*t` instead of `let x = *t`. – Kyle Jun 29 '18 at 20:34
  • Also fun fact: as of Rust 1.26 you don't need to dereference in a `match` anymore (though you may choose to for people using older Rust versions). – Linear Jun 29 '18 at 22:27

1 Answers1

0

let x = *t does not work, because it's moved out of the reference. I.e. this means, with dereferencing t, you get a BinaryTree (without reference). The BinaryTree is borrowed in the function, so assigning it with let x = *t will move the it into the x, which is not possible, since it's borrowed.

The match does work, because match borrows the variable. match *t will not move it out, but it will borrow the BinaryTree. This is a special syntax in Rust and the borrow happens behind the scenes. You can think of this:

fn tree_weight_v2(t: &BinaryTree) -> i32 {
    match &*t {
        &BinaryTree::Leaf(payload) => payload,
        &BinaryTree::Node(ref left, payload, ref right) => {
            tree_weight_v2(left) + payload + tree_weight_v2(right)
        }
    }
}

but all the references are not needed.


See also:

Tim Diekmann
  • 7,755
  • 11
  • 41
  • 69