Lifetime when pusing pointer into vector


#1

Is it possible to push a pointer into a Vec in the following manner?

fn foo(v: &mut Vec<&T>) -> Result<(), io::Error> {
    let e = try!(some_io_that_may_return_a_T());
    v.push(&e);
    Ok(())
}

The borrow checker is telling me that e does not live long enough which I think I understand. That would be a borrow which would be dead after foo is done executing.

Is there a way to specify the lifetime so that the pointer to e survives?

The following works but it’s copying e so that the vector takes ownership and I want to avoid the copy.

fn foo(v: &mut Vec<T>) -> Result<(), io::Error> {
    let e = try!(some_io_that_may_return_a_T());
    v.push(e);
    Ok(())
}

#2

You should box it if you want to avoid the copy:

fn foo(v: &mut Vec<Box<T>>) -> Result<(), io::Error> {
    let e = try!(some_io_that_may_return_a_T());
    v.push(Box::new(e));
    Ok(())
}

The above actually is just as bad, but

fn foo(v: &mut Vec<Box<T>>) -> Result<(), io::Error> {
    let e = Box::new(try!(some_io_that_may_return_a_T()));
    v.push(e);
    Ok(())
}

shouldn’t have that extra copy.

&T is a reference and will be tied to the scope of creation. Box<T> is the “owned” pointer which can be moved around.


#3

I want to clarify that lifetime parameters describe and do not prescribe how long a value is valid to keep; so this is not possible.


#4

Try to think about & as link to something which have to exists. Pointers aren’t real objects as it sometimes happens with C/C++. If you have & you also have an object somewhere. Boxing above is good solution.

Sometimes I’m thinking that wasn’t good idea to use & as borrowing operator, anybody confused it, but my fingers have accustomed to type that symbol, and really it isn’t a bad idea to use &.


#5

I’ve been bogged down with other projects and couldn’t get back to this. Thanks for the replies.

@bluss

I don’t understand the implications of your statement and I have so many questions about it. Her are a few.

Is the description used by the compiler?

Is the following accurate?

We write code that has bindings. Every binding has a lifetime. Some lifetimes can be inferred by the compiler. Some must be described to the compiler.

If so, why do we have to describe some lifetimes?

@DenisKolodin

I always considered the & operator in Rust as a super powered version of the same operator in C++. In Rust it has a superset of the C++ features in that it not only is the address-of operator but also defines a lifetime beyond a binding’s local scope.

Is that not correct?


#6

The primary reason explicit syntax for lifetimes is necessary is to make sure lifetime checking the body of one function doesn’t depend on the body of any other functions. This is good for many reasons: it allows documenting the lifetime bounds of functions and types, it makes compiler error messages easier to understand, it allows functions containing unsafe code to enforce lifetimes for the inputs and outputs, and it makes it easier to write the compiler.


#7

That’s correct. But semantic has much difference. Look at this:

// Rust version
fn main() {
    let first = 10;
    let second = &first;
    let third = first + second;
    println!("{}", third);
}
// C version
int main() {
    int first = 10;
    int* second = &first;
    int third = first + *second;
    printf("%d", third);
}

With C case reference has produced a really another type, but with Rust you have the same type was borrowed. You can imagine that the reference lays deeper and can’t be used safety as independent unit.

For your example above lifetimes can track the data have lost. Rust is still low-level language and you have to choose where to store the data: in stack or heap. In cases of changing owners data also can be copied, but you can use unsafe tools to change that.


#8

Thank you for the explanations. The topic is much more clear now.