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

1

10

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?

Dripdry answered 14/1, 2020 at 8:27 Comment(3)
Relevant link - rust-unofficial.github.io/too-many-lists In short, yes, your suggestion of "structuring differently" is correct, although I'd say not about code but about data: in Rust it's important which name is owning each data.Impediment
Thanks; I will definitely have a look!Dripdry
It's hard to answer your question because it doesn't include a minimal reproducible example. The code you have provided does not produce the error you say it does. It would make it easier for us to help you if you try to reproduce your error on the Rust Playground then edit your question to include the additional info. There are Rust-specific MRE tips you can use to reduce your original code for posting here. Thanks!Tinny
A
8

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.

Aeneas answered 14/1, 2020 at 8:38 Comment(2)
Thank you very much; I had tried adding muts to everything earlier but got an error I cannot reproduce anymore (probably forgot something). Anyway, it works now :)Dripdry
You can also match mutably - "match &mut last { Some(last) => ..."Schulein

© 2022 - 2024 — McMap. All rights reserved.