Search code examples
referencerustmutableborrowing

Elegant way to borrow and return a mutable reference in Rust


I'm trying to return a mutable reference after doing some operation on it. This is best explained by a piece of code:

#[derive(PartialEq)]
pub enum Value {
    Null,
    Array(Vec<Value>),
}

impl Value {
    pub fn new() -> Value {
        Value::Array(Vec::new())
    }

    pub fn push<'a, T> (&'a mut self, value: T) -> Option<&'a mut Value>
    where T:Into<Value> {
        let temp = match *self {
            Value::Array(ref mut vec) => {
                vec.push(value.into());
                true
            },
            _ => false,
        };
        if temp {
            Some(self)
        } else {
            None
        }
    }
}

#[test]
fn push_test() {
    let mut val = Value::new();
    val.push(Value::Null);
    assert!(val == Value::Array(vec![Value::Null]));
}

The play version is here. The workaround with boolean values is because I would be borrowing multiple times if I return Some(self) from within the match block. Is there an elegant way to implement the push function without using boolean values? If its possible to retain the function signature then its a bonus. Thank you!


Solution

  • The workaround with boolean values is because I would be borrowing multiple times if I return Some(self) from within the match block

    Another option is to replace self temporally, so v can take the ownership of the vector (avoiding the borrow). After adding the new item to v, we reconstruct the self value:

    // the lifetime 'a can be omitted
    pub fn push<T>(&mut self, value: T) -> Option<&mut Value>
        where T: Into<Value>
    {
        // replace put Value::Null on self and return the old value
        match ::std::mem::replace(self, Value::Null) {
            Value::Array(mut v) => {
                v.push(value.into());
                *self = Value::Array(v);
                Some(self)
            },
            _ => None,
        }
    }