Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"borrowed value does not live long enough" when using a struct with a slice

Editor's note: The code in this question predates Rust 1.0. Since then, semantics have changed and some of the assertions made in the question are no longer true.

I have the following piece of code:

extern crate debug;

use std::mem::size_of_val;

struct A<'a> {
    a: &'a [i64],
}

fn main() {
    // code
}

When I define a slice with & (i.e. &[1, 2, 3]) as in the following println!

println!("{} - {:?}", size_of_val(&A { a: &[1, 2, 3] }), A { a: &[1, 2, 3] });

the output is

16 - A<'static>{a: &[1i64, 2i64, 3i64]}

Defining a slice without &

println!("{} - {:?}", size_of_val(&A { a: [1, 2, 3] }), A { a: [1, 2, 3] });

gives me the same result

16 - A<'static>{a: &[1i64, 2i64, 3i64]}

If I first try to bind an instance of a struct A, whose a field is initialized with a reference to a slice (i.e. using &), to a variable x

let x = A { a: &[1, 2, 3] }; // &[1, 2, 3] is a reference to a slice

and I try to execute a similar println! as the previous ones

println!("{} - {:?}", size_of_val(&x), x);

I get

16 - A<'static>{a: &[1i64, 2i64, 3i64]}

However, if I bind an instance of A, whose a field is initialized to a slice (not a reference to a slice using &), to a variable x

let x = A { a: [1, 2, 3] };

and I try to execute a similar println! as the previous ones

println!("{} - {:?}", size_of_val(&x), x);

I get the following build error:

/prpath/main.rs:12:20: 12:29 error: borrowed value does not live long enough
/prpath/main.rs:12     let x = A { a: [1 ,2, 3] };
                                        ^~~~~~~~~
/prpath/main.rs:11:11: 15:2 note: reference must be valid for the block at 11:10...
/prpath/main.rs:11 fn main() {
/prpath/main.rs:12     let x = A { a: [1 ,2, 3] };
/prpath/main.rs:13 
/prpath/main.rs:14     println!("{} - `{:?}`", size_of_val(&x), x);
/prpath/main.rs:15 }
/prpath/main.rs:12:5: 12:31 note: ...but borrowed value is only valid for the statement at 12:4; consider using a `let` binding to increase its lifetime
/prpath/main.rs:12     let x = A { a: [1 ,2, 3] };
                        ^~~~~~~~~~~~~~~~~~~~~~~~~~
error: aborting due to previous error

I was expecting that only the A { a: &[1, 2, 3] } definition was allowed because A.a should have &[i64] type, but, apparently, Rust allows us to not include an & symbol.

What is the difference between A { a: &[1, 2, 3] } and A { a: [1, 2, 3] }? Why are we allowed to use A { a: [1, 2, 3] } (in the second example above)?

like image 589
tbicr Avatar asked Oct 17 '14 16:10

tbicr


1 Answers

First, you can use a [T,..n] where a &[T] is expected, the conversion to a slice is implicit. So following code is perfectly valid:

let a = [1u, 2, 3];
let b: &[uint] = a;

Your situation is a purely lifetime problem. Your struct is

struct A<'a> {
    a: &'a [i64],
}

It holds a slice. A slice is nothing more than a reference to the first element and a count of the number of elements. That's why size_of_val() called on a A will always return 16: it's the size of a slice, one u64 for the pointer, and one u64 for the number of elements (as you seems to be on a 64bits computer).

So in your code, the struct does not own the array. The difference in behavior you observe is due to the difference about when the array does out of scope.

First case:

let x = A { a: [1, 2, 3] };

Here you define an array, and stores a slice to this array in your struct. Then when reaching the ;, your array does out of scope and is destroyed, and thus the reference in x is not valid anymore: it is forbidden by the compiler.

Second case:

let x = A { a: &[1, 2, 3] };

It is more peculiar. Your array is stored in an anonymous variable. In fact, writing

let foo = &42u;

is equivalent to writing

let _anonymousvariable = 42u;
let foo = &_anonymousvariable;

Except that you can't reach _anonymousvariable directly.

It's exactly the same for you, your code is equivalent to

let _anonymous_array = [1, 2, 3]
let x = A { a: &_anonymous_array };

and thus is perfectly valid.

Last case:

When you write everything directly in the println!(). Thanks to previous case, we now see why this works:

println!("{} - {:?}", size_of_val(&A { a: &[1, 2, 3] }), A { a: &[1, 2, 3] });

but in this case there is no problem either:

println!("{} - {:?}", size_of_val(&A { a: [1, 2, 3] }), A { a: [1, 2, 3] });

because your arrays only go out of scope when reaching the ;, and no reference to them exist past this point, so they can be safely deleted and the compiler is happy.

like image 73
Levans Avatar answered Sep 30 '22 00:09

Levans