0

I'm building a little program to learn rust. I'm trying to make snake.

Snake consists of snake parts. Those snake parts work like linked list, each snake part knows which snake part is the previous one. Snake itself knows where is it's head and tail. It looks like this:

pub struct Snake<'a> {
    head: &'a SnakePart<'a>,
    tail: &'a SnakePart<'a>,
}

struct SnakePart<'a> {
    x: f32,
    y: f32,
    previous_part: Option<&'a SnakePart<'a>>,
}

However I have problems writing Snake constructor. So far I have this:

impl<'a> Snake<'a> {
    pub fn new(x: f32, y: f32) -> Snake<'a> {
        let snake_part: SnakePart<'a> = SnakePart::new(x, y);
        Snake {
            head: &snake_part,
            tail: &snake_part,
            current_angle: 0.,
        }
    }
}

This creates snake, which has only one segment (or snake part), and it's both his tail and head. However this doesn't compile. Compiler tells me that snake_part only lives to the end of constructor, and it has to live for the lifetime 'a as defined on the impl block.

I know that it has to live as long as the whole snake, but why this does not work? I would think that the snake part will live while the snake itself live - the snake I return has lifetime 'a. How can I persuade Rust to let snake parts live long enough?


I tried to save all snake parts into the snake structure itself. So I changed it this way:

pub struct Snake<'a> {
    ...
    snake_parts: Vec<SnakePart<'a>>,
}

impl<'a> Snake<'a> {
        let snake_part: SnakePart<'a> = SnakePart::new(x, y);
        let mut snake_parts: Vec<SnakePart<'a>> = Vec::with_capacity(1);
        snake_parts.push(snake_part);
        Snake {
            head: &snake_parts[0],
            tail: &snake_parts[0],
            snake_parts: snake_parts,
            current_angle: 0.,
        }
}

Why this doesn't work? (It has exactly the same problem as the previous code). I take the snake part, push it into vector full of snake parts, and the vector itself is saved into the snake. Why doesn't it live long enough?

SoptikHa
  • 437
  • 4
  • 19
  • 1
    I suggest you read [Learn Rust With Entirely Too Many Linked Lists](https://rust-unofficial.github.io/too-many-lists/) – Jmb Jun 28 '19 at 14:43
  • I actually found this when I was trying to find out where is the mistake, but I didn't start reading it. I guess I'll study it now. Thanks! – SoptikHa Jun 28 '19 at 14:48
  • Possible duplicate of [Is there any way to return a reference to a variable created in a function?](https://stackoverflow.com/questions/32682876/is-there-any-way-to-return-a-reference-to-a-variable-created-in-a-function) You're wrapping them in a `struct`, but fundamentally still trying to return references to locals, which doesn't make sense. – trent Jun 28 '19 at 18:13
  • Also see [Why can't I store a value and a reference to that value in the same struct?](https://stackoverflow.com/questions/32300132/why-cant-i-store-a-value-and-a-reference-to-that-value-in-the-same-struct) for the second part. – trent Jun 28 '19 at 18:16
  • Thanks for the link. This is something I want to highlight from the answer: “A lifetime is a bit of metadata that allows you and the compiler to know how long a value will be valid at its current memory location. That's an important distinction, as it's a common mistake Rust newcomers make. Rust lifetimes are not the time period between when an object is created and when it is destroyed!” – SoptikHa Jun 29 '19 at 20:44

1 Answers1

2

I ended up using Vec as kind of linked list, so I don't have to implement my own. If you are reading this, I strongly recommend you to read Learn Rust With Entirely Too Many Linked Lists as @Jmb suggested.

This solution kind of avoids the issue altogether, so I won't mark this answer as a solution. The solution is in the guide linked above. However if you have similar problem, it's probably possible to solve your problem with similar solution as is this one - just use Vec, don't build it on your own.

I just did this and pop and push from snake_body when moving the snake.

pub struct Snake {
    /// Snake body. The LAST item is head, the FIRST item is tail.
    snake_body: Vec<SnakePart>,
}

struct SnakePart {
    x: f32,
    y: f32,
}
SoptikHa
  • 437
  • 4
  • 19