How to express lifetime constraints for closures which take and return references?

Advertisements

I have sketched out an example problem in the Rust playground.

For reference, I will provide the full code at the end of this question.

I have attempted to write the following function.

fn get_longest_time_window(&self) -> Option<u64> {
        
        let lambda = |lhs: &Element, rhs: &Element| -> &Element {
            
            return
                if lhs.time_window >= rhs.time_window {
                    lhs
                }
                else {
                    rhs
                }
        };
        
        let selected = self.data.iter().reduce(lambda);

        // ...

This function operates on self.data. I have attempted to iterate over this datastructure and apply a reduce closure to it.

This closure takes references to pairs of elements in data, and returns a reference to one of them. The returned reference will still be valid at the end of iteration, as none of the data in memory has been removed or otherwise changed causing a reallocation which would invalidate the references.

selected should contain an Option<&Element>

The above suggests that this code should be valid, but it does not compile because the closure talks about 3 references and doesn’t know what the relative lifetimes between them are.

I would guess the lifetimes should all be the same, but I don’t know how to express that in Rust syntax.

Is what I am attempting to write valid, and if so, what can be done to make it compile?


Code:

use std::collections::BTreeSet;

struct Element {
    
    pub data: String, // arbitrary
    pub time_window: u64,
}

struct Machine {
    
    data: BTreeSet<Element>
}

impl Machine {

    fn new() -> Machine {
        
        let machine = Machine {
            data: BTreeSet::new()
        };
        
        return machine;
    }
    
    fn get_longest_time_window(&self) -> Option<u64> {
        
        let lambda = |lhs: &Element, rhs: &Element| -> &Element {
            
            return
                if lhs.time_window >= rhs.time_window {
                    lhs
                }
                else {
                    rhs
                }
        };
        
        let selected = self.data.iter().reduce(lambda);
    
        return selected.map(|x| {x.time_window});
    }
}


fn main() {

    let machine = Machine::new();
    
    let max_time_window = machine.get_longest_time_window();
    
    println!("max_time_window = {:?}", max_time_window);
}

>Solution :

In your case you don’t really need a closure as it doesn’t capture any of the environment, a regular function will work:

fn get_longest_time_window(&self) -> Option<u64> {
    fn lambda<'a>(lhs: &'a Element, rhs: &'a Element) -> &'a Element {
        return if lhs.time_window >= rhs.time_window {
            lhs
        } else {
            rhs
        };
    }
    //…
}

Once closure_lifetime_binder stabilizes or on nightly you can use them to specify the same on a closure:

#![feature(closure_lifetime_binder)]
//…
let lambda = for<'a> |lhs: &'a Element, rhs: &'a Element| -> &'a Element { /* … */ };

Leave a ReplyCancel reply