Temporary value does not live long enough


#1

Hi All,

I was experimenting with crate bincode and I encountered this error: “temporary value does not live long enough” when I used config this way:

let ser = config().big_endian(); // This would not compile.

but if I create a local variable like conf (below code), it compiles.

    let mut conf = config();
    let ser = conf.big_endian();

Any explanation for this behavior?

Error detail:
error[E0597]: borrowed value does not live long enough

  • temporary value dropped here while still borrowed
    temporary value does not live long enough

#2

Looks like big_endian() returns a serializer borrowing from the config; if so, the config needs to be kept alive while the serializer is. In your first case, config() returns a config that’s going to be dropped on that line since it’s a temporary and not kept alive by anything.


#3

This is the definition of big_endian:

    pub fn big_endian(&mut self) -> &mut Self {
    self.endian= EndianOption::Big;
    self
}

but why is that temporary? That is assigned to “ser” in let ser = …


#4

&mut Self return means you’re holding a mutable borrow of it, but we need an owner to borrow from; the owner here, result of config(), is a temp that dies.


#5

That was my question, why does the config die?


#6

The compiler doesn’t keep temps alive in constructs like this. Your ser is a &mut Serializer, which as mentioned means you’re borrowing the Serializer mutably from somewhere else. That somewhere else needs to be alive.

Rust does allow constructs like this though:

let v = &mut vec![1,2,3];

I’m not sure how it’s actually implemented in the compiler, but you can imagine a desugaring like:

let mut v = vec![1,2,3];
let v = &mut v;

But it doesn’t do that for cases like you have.


#7

By the way, are config() and big_endian() code you control?


#8

No, they are part of “bincode”.

In your example:

 let v = &mut vec![1,2,3];

if I modify that to:

let v = &mut vec![1,2,3].as_mut_slice();

where as_mut_slice has a similar signature to big_endian, then the same error would be raised.
Which essentially means that builder pattern would not work.

It is interesting that if I cascade that “temporary” to serialize function then there would no problem and it would compile.

let mut bytes = config().big_endian().serialize(&req)?;

#9

That’s because bytes has no references and so the “temp dropped at statement end” is ok.

The issue of temp (rvalue) lifetimes is a known papercut. The case I’ve seen reported a few times in particular is usage of std::io::stdout().lock()...

https://github.com/nikomatsakis/rfcs/blob/rfc66-amendment/text/0066-better-temporary-lifetimes.md talks about this some more.

Also, this might be a reason to prefer the builder pattern where you take and return self by value, rather than reference (that’s what I was going to suggest if this was your code).