Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

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

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.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

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 { /* … */ };
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading