Reputation: 89
I'm new to Rust, and have the following code which doesn't compile:
fn cells(&self) -> impl Iterator<Item = &Vector2<isize>> {
match self {
Kind::O => [(0, 0), (0, 1), (1, 0), (1, 1)],
Kind::I => [(-1, 0), (0, 0), (1, 0), (2, 0)],
Kind::T => [(-1, 0), (0, 0), (1, 0), (0, 1)],
Kind::L => [(-1, 0), (0, 0), (1, 0), (1, 1)],
Kind::J => [(-1, 1), (-1, 0), (0, 0), (1, 0)],
Kind::S => [(-1, 0), (0, 0), (0, 1), (1, 1)],
Kind::Z => [(-1, 1), (0, 1), (0, 0), (1, 0)],
}
.iter()
.map(From::from)
}
the compiler complains:
cannot return value referencing temporary value
I kind of understand that it's because the array will be dropped when the function returns.
but I cannot understand why the code compiles when we change it to the following:
fn cells(&self) -> impl Iterator<Item = &Vector2<isize>> {
match self {
Kind::O => &[(0, 0), (0, 1), (1, 0), (1, 1)],
Kind::I => &[(-1, 0), (0, 0), (1, 0), (2, 0)],
Kind::T => &[(-1, 0), (0, 0), (1, 0), (0, 1)],
Kind::L => &[(-1, 0), (0, 0), (1, 0), (1, 1)],
Kind::J => &[(-1, 1), (-1, 0), (0, 0), (1, 0)],
Kind::S => &[(-1, 0), (0, 0), (0, 1), (1, 1)],
Kind::Z => &[(-1, 1), (0, 1), (0, 0), (1, 0)],
}
.iter()
.map(From::from)
}
which only changes the array to a slice.
Upvotes: 0
Views: 1534
Reputation: 58695
In both examples, the array literals are actually treated as static. That is, they are kept in memory that is valid for the duration of the program.
In the first example, in each match arm, you copy an array onto the stack. That's because the match
expression must evaluate to something and its type is [(isize, isize); 4]
, because you didn't take a reference. You cannot return a reference to this value because it lives on the stack, and therefore references to it will be invalid after the function returns. The original array still lives in static memory but the reference you are trying to return is not pointing at that, it's pointing at the copy on the stack.
In the second example, you are only ever working with references to the arrays. Since the arrays are in static memory, these references are valid forever; i.e. they have type &'static [(isize, isize); 4]
.
Introducing another variable makes it clearer what is happening:
fn cells(&self) -> impl Iterator<Item = &Vector2<isize>> {
// array is local and its type is [(isize, isize); 4]
let array = match self {
Kind::O => [(0, 0), (0, 1), (1, 0), (1, 1)],
Kind::I => [(-1, 0), (0, 0), (1, 0), (2, 0)],
Kind::T => [(-1, 0), (0, 0), (1, 0), (0, 1)],
Kind::L => [(-1, 0), (0, 0), (1, 0), (1, 1)],
Kind::J => [(-1, 1), (-1, 0), (0, 0), (1, 0)],
Kind::S => [(-1, 0), (0, 0), (0, 1), (1, 1)],
Kind::Z => [(-1, 1), (0, 1), (0, 0), (1, 0)],
};
// You can't return references to the data because items are owned by
// the local variable
array.iter().map(From::from)
}
Using references:
fn cells(&self) -> impl Iterator<Item = &Vector2<isize>> {
// array is local but its type is &'static [(isize, isize); 4]
let array = match self {
Kind::O => &[(0, 0), (0, 1), (1, 0), (1, 1)],
Kind::I => &[(-1, 0), (0, 0), (1, 0), (2, 0)],
Kind::T => &[(-1, 0), (0, 0), (1, 0), (0, 1)],
Kind::L => &[(-1, 0), (0, 0), (1, 0), (1, 1)],
Kind::J => &[(-1, 1), (-1, 0), (0, 0), (1, 0)],
Kind::S => &[(-1, 0), (0, 0), (0, 1), (1, 1)],
Kind::Z => &[(-1, 1), (0, 1), (0, 0), (1, 0)],
};
// Now it's ok to return references because the data is still just
// static references to the data produce by the original literals
array.iter().map(From::from)
}
Upvotes: 1