Lifetime error when pushing value into a generic type

How can I avoid this lifetime error:

error[E0597]: `s` does not live long enough

Storage doesn't have a lifetime parameter, so s is not used after storage.push(&s);

trait PushValue<T> {
    fn push(&mut self, value: T);
}

fn push_something<'a, S: PushValue<&'a str>>(storage: &mut S) {
    let s = format!("something {}", 234);
    storage.push(&s);
}

// struct DefaultStorage {
//     string: String
// }

// impl<'a> PushValue<&'a str> for DefaultStorage {
//     fn push(&mut self, value: &'a str) {
//         self.string.push_str(value);
//     }
// }

Playground link: Rust Playground

To fix the immediate compile time error you can use an HRTB:

trait PushValue<T> {
    fn push(&mut self, value: T);
}

fn push_something<S>(storage: &mut S) where for<'a> S: PushValue<&'a str> {
    let s = format!("something {}", 234);
    storage.push(&s);
}

Playground.

However, this will not change the fact that s is dropped at the end of the function. You can't store a reference to a function-local variable in a container passed from outside of the function, it would dangle the moment that the function returns.

4 Likes

Thanks, HRTB seems to do what I wanted.
I know it is dropped at the end of the function, but I'm not using it after that.

Ah yes, I just saw the commented-out DefaultStorage type in your snippet. String::push_str doesn't store the reference for later access, but clones the underlying bytes to an owned location instead, avoiding any use-after-frees.

1 Like

To explain the error, lifetimes nameable in a function, such as lifetimes defined on the function...

//                vv
fn push_something<'a, S: PushValue<&'a str>>(storage: &mut S) {

...represent borrow durations at least just longer than the function body. As such, you can never borrow a local for that duration, because all locals move or drop by the time the function body ends -- which is incompatible with being borrowed.

So you can't borrow s for 'a with the original signature.

HRTBs are how we write bounds that can work with borrows of locals (borrows shorter than the function body).

2 Likes