Search code examples
rustreferenceassignmutable

How do I handle/circumvent "Cannot assign to ... which is behind a & reference" in Rust?


I'd implementing a simple linked list. This is the (working) code I had so far:

pub struct LinkedList<T> {
    start: Option<Box<Link<T>>>,
}

impl<T> LinkedList<T> {
    pub fn new() -> LinkedList<T> {
        return LinkedList { start: None };
    }
}

struct Link<T> {
    value: Box<T>,
    next: Option<Box<Link<T>>>,
}

impl<T> Link<T> {
    fn new_end(value: T) -> Link<T> {
        return Link::new(value, None);
    }

    fn new(value: T, next: Option<Box<Link<T>>>) -> Link<T> {
        return Link {
            value: Box::new(value),
            next,
        };
    }
}

Next on the list is a method to append to the list; this is what I came up with:

pub fn append(&mut self, element: T) {
    // Create the link to append
    let new_link = Some(Box::new(Link::new_end(element)));

    // Find the last element of the list. None, if the list is empty
    let mut last = &self.start;
    while let Some(link) = last {
        last = &link.next;
    }

    // Insert the new link at the correct position
    match last {
        None => self.start = new_link,
        Some(last) => last.next = new_link, // This fails
    }
}

The precise compiler error is

error[E0594]: cannot assign to `last.next` which is behind a `&` reference

I vaguely get the problem; you cannot mutate an immutable reference. But making the references mutable does seem to make the errors even worse.

How does one handle these kinds of errors? Is there a simple quick-fix, or do you structure your code completely different in Rust?


Solution

  • Your code almost worked. It will if you bind mutably:

    impl<T> LinkedList<T> {
        pub fn append(&mut self, element: T) {
            // Create the link to append
            let new_link = Some(Box::new(Link::new_end(element)));
    
            // Find the last element of the list. None, if the list is empty
            let mut last = &mut self.start;
            while let Some(link) = last {
                last = &mut link.next;
            }
    
            // Insert the new link at the correct position
            match last {
                None => self.start = new_link,
                Some(ref mut last) => last.next = new_link,
            }
        }
    }
    

    FYI, the answer to this recent question is very good at clarifying the matter about mutability, type and binding in Rust.