Turn compile-time lifetime check into runtime

I am not sure if I had came up with an accurate title.

I want to continuously receive user input and get its reference, then proceed to request the next user input. However, I am looking for a way to ensure that all user input values have a fixed lifetime, lasting until the end of the function.

This issue arises when using the wgpu library. The RenderPass class's member functions require that the lifetime of the references in the arguments should be at least as long as the RenderPass's lifetime parameter. (e.g. set_pipeline)

// imagine: this struct was provided by an external library.
struct Limiter<'a> {
    internal: &'a i32,
}

impl<'a> Limiter<'a> {
    fn new(internal: &'a i32) -> Self {
        Limiter { internal }
    }

    fn set(&mut self, reference: &'a i32) {
        self.internal = reference;
    }
}

// imagine: this function would get a value from console
fn get_value_from_console() -> i32 {
    0
}

fn main() {
    let initial = 0;
    let mut limiter = Limiter::new(&initial);

/*
    // Attemp #1: failed. borrowed value does not live long enough
    let mut value_keeper: Vec<i32> = Vec::new();
    for _ in 0..100 {
        let mut value = get_value_from_console();
        limiter.set(&mut value);
    }
*/

/*
    // Attemp #2: failed. cannot borrow `value_keeper` as mutable because it is also borrowed as immutable
    let mut value_keeper: Vec<i32> = Vec::new();
    for _ in 0..100 {
        value_keeper.push(get_value_from_console());
        limiter.set(value_keeper.last().unwrap());
    }
*/

    // Attemp #3: succeeded. But I have to wait for all inputs before invoking `limiter.set`
    let zeros = (0..100).map(|_| get_value_from_console()).collect::<Vec<_>>();
    for i in 0..100 {
        limiter.set(&zeros[i]);
    }
}

An append-only arena type can help.


use typed_arena::Arena;

fn main() {
    let initial = 0;
    let mut limiter = Limiter::new(&initial);
    let value_keeper: Arena<i32> = Arena::new();
    for _ in 0..100 {
        limiter.set(value_keeper.alloc(get_value_from_console()));
    }
}

(run onrustexplorer.com)


Attempt 1 cannot work, as there’s no guarantee from the set function’s signature that subsequent set operations will get rid of the data from previous calls, so you cannot simply drop the relevant (i32) data that was referenced in those previous calls.

1 Like

Great help! This is exactly the crate that I want. But it would be even better if there were a similar container in the standard library.

That is unlikely to happen anytime soon; Having learned a lot of lessons from both Python as well as NodeJS, the consensus is that by far most libraries are better off permanently in crates.

To see why, Python's stdlib is a good case study. It contains a lot of functionality, and a not insignificant chunk of that has been replaced by better code in libraries over time, and even deprecated.
I don't think I need to explain why that is unhelpful.

But given that situation, you may as well cut out the middle steps and just go to the end stage, which is where Rust has ended up.

3 Likes