dubugger
dubugger

Reputation: 109

How to assign new value to an Option<Box<_>> variable?

I am writing a tree in Rust and I want to implement a function:left which can turn a tree's root into the tree's root's left child. Here is my code:

struct Node {
    value: i32,
    left: Option<Box<Node>>,
    right: Option<Box<Node>>,
}

struct Tree {
    root: Option<Box<Node>>,
}

enum Error {
    DuplicateValue,
    NotFound,
    EmptyTree
}

impl Tree {
    fn left(&mut self) -> Result<(), Error> {
        match self.root {
            Option::None => return Err(Error::EmptyTree),
            Option::Some(ref mut node) => {
                *node = node.left.as_ref().unwrap();
                return Ok(())
            }
        }
    }
}

and the error says

mismatched types
expected struct `Box<Node>`
found reference `&Box<Node>`

I have tried many methods and look up a lot of information but I still cannot fix this. I think it is a very simple thing in C, just like:

*root = *(root->left);

but why is it so hard in Rust? Can anyone help me?

Upvotes: 4

Views: 1634

Answers (3)

FZs
FZs

Reputation: 18619

You probably want to replace the entire Option in self.root, not just the value inside the Some() (so that even when left is None, you don't panic but assign it to self.root).

The second problem you're facing is that you have to move the left branch out from the old root in order to be able to give its ownership to self.root. Normally, you wouldn't be able to do that, since you only have a mutable reference. Fortunately Option::take() is here to help: it lets you take ownership of a value in a &mut Option, changing it to None.

Now that you own the old tree, you can easily assign its left subtree to self.root.

Finally, instead of the match thing you had, you can just convert the Option into a Result via Option::ok_or() and short-circuit or extract the value with ?:

fn left(&mut self) -> Result<(), Error> {
    let old_root = self.root.take().ok_or(Error::EmptyTree)?;
    self.root = old_root.left;
    Ok(())
}

Upvotes: 1

Cerberus
Cerberus

Reputation: 10218

Since your code looks like it throws away the whole right branch and the root node when calling left, i.e. you aren't concerned with the original root (it will be dropped after the assignment), you can simply take the left node out of the Option:

impl Tree {
    fn left(&mut self) -> Result<(), Error> {
        match self.root {
            Option::None => Err(Error::EmptyTree),
            Option::Some(ref mut node) => {
                *node = node.left.take().unwrap();
                Ok(())
            }
        }
    }
}

Playground

Note however that this will panic if the left subtree is empty. If you want to make self empty in this case, you want to replace the whole Option, not the value inside it. In this case, you have to first do the matching, return early in the empty case, and only then, after the root is no longer borrowed, do the replacement:

impl Tree {
    fn left(&mut self) -> Result<(), Error> {
        let node = match self.root {
            Option::None => return Err(Error::EmptyTree),
            Option::Some(ref mut node) => node.left.take(),
        };
        self.root = node;
        Ok(())
    }
}

Upvotes: 5

Mohamad-Jaafar NEHME
Mohamad-Jaafar NEHME

Reputation: 1215

Use clone() with #[derive(Clone)]

#[derive(Clone)]
struct Node {
    value: i32,
    left: Option<Box<Node>>,
    right: Option<Box<Node>>,
}

struct Tree {
    root: Option<Box<Node>>,
}

enum Error {
    DuplicateValue,
    NotFound,
    EmptyTree
}

impl Tree {
    fn left(&mut self) -> Result<(), Error> {
        match self.root {
            Option::None => return Err(Error::EmptyTree),
            Option::Some(ref mut node) => {
                *node = node.left.clone().unwrap();
                return Ok(())
            }
        }
    }
}

Upvotes: -1

Related Questions