9

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?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Islion
  • 111
  • 1
  • 6
  • 2
    Relevant link - https://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. – Cerberus Jan 14 '20 at 08:34
  • 1
    Thanks; I will definitely have a look! – Islion Jan 14 '20 at 09:06
  • It's hard to answer your question because it doesn't include a [MRE]. The code you have provided [does not produce the error you say it does](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=4502e208e771b8331b9212018a2f5e1d). It would make it easier for us to help you if you try to reproduce your error on the [Rust Playground](https://play.rust-lang.org) then [edit] your question to include the additional info. There are [Rust-specific MRE tips](//stackoverflow.com/tags/rust/info) you can use to reduce your original code for posting here. Thanks! – Shepmaster Jan 14 '20 at 15:23

1 Answers1

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.

edwardw
  • 12,652
  • 3
  • 40
  • 51
  • 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 :) – Islion Jan 14 '20 at 09:05
  • You can also match mutably - "match &mut last { Some(last) => ..." – schellsan Jan 14 '20 at 19:01